GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/external/miniaudio.h
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 12052 0.0%
Branches: 0 8834 0.0%

Line Branch Exec Source
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.16 - 2023-05-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); // This will stop the device so no need to do that manually.
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
542 2.7. Build Options
543 ------------------
544 `#define` these options before including miniaudio.h.
545
546 +----------------------------------+--------------------------------------------------------------------+
547 | Option | Description |
548 +----------------------------------+--------------------------------------------------------------------+
549 | MA_NO_WASAPI | Disables the WASAPI backend. |
550 +----------------------------------+--------------------------------------------------------------------+
551 | MA_NO_DSOUND | Disables the DirectSound backend. |
552 +----------------------------------+--------------------------------------------------------------------+
553 | MA_NO_WINMM | Disables the WinMM backend. |
554 +----------------------------------+--------------------------------------------------------------------+
555 | MA_NO_ALSA | Disables the ALSA backend. |
556 +----------------------------------+--------------------------------------------------------------------+
557 | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. |
558 +----------------------------------+--------------------------------------------------------------------+
559 | MA_NO_JACK | Disables the JACK backend. |
560 +----------------------------------+--------------------------------------------------------------------+
561 | MA_NO_COREAUDIO | Disables the Core Audio backend. |
562 +----------------------------------+--------------------------------------------------------------------+
563 | MA_NO_SNDIO | Disables the sndio backend. |
564 +----------------------------------+--------------------------------------------------------------------+
565 | MA_NO_AUDIO4 | Disables the audio(4) backend. |
566 +----------------------------------+--------------------------------------------------------------------+
567 | MA_NO_OSS | Disables the OSS backend. |
568 +----------------------------------+--------------------------------------------------------------------+
569 | MA_NO_AAUDIO | Disables the AAudio backend. |
570 +----------------------------------+--------------------------------------------------------------------+
571 | MA_NO_OPENSL | Disables the OpenSL|ES backend. |
572 +----------------------------------+--------------------------------------------------------------------+
573 | MA_NO_WEBAUDIO | Disables the Web Audio backend. |
574 +----------------------------------+--------------------------------------------------------------------+
575 | MA_NO_NULL | Disables the null backend. |
576 +----------------------------------+--------------------------------------------------------------------+
577 | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to |
578 | | enable specific backends. |
579 +----------------------------------+--------------------------------------------------------------------+
580 | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
581 | | enable the WASAPI backend. |
582 +----------------------------------+--------------------------------------------------------------------+
583 | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
584 | | enable the DirectSound backend. |
585 +----------------------------------+--------------------------------------------------------------------+
586 | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
587 | | enable the WinMM backend. |
588 +----------------------------------+--------------------------------------------------------------------+
589 | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
590 | | enable the ALSA backend. |
591 +----------------------------------+--------------------------------------------------------------------+
592 | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
593 | | enable the PulseAudio backend. |
594 +----------------------------------+--------------------------------------------------------------------+
595 | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
596 | | enable the JACK backend. |
597 +----------------------------------+--------------------------------------------------------------------+
598 | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
599 | | enable the Core Audio backend. |
600 +----------------------------------+--------------------------------------------------------------------+
601 | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
602 | | enable the sndio backend. |
603 +----------------------------------+--------------------------------------------------------------------+
604 | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
605 | | enable the audio(4) backend. |
606 +----------------------------------+--------------------------------------------------------------------+
607 | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
608 | | enable the OSS backend. |
609 +----------------------------------+--------------------------------------------------------------------+
610 | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
611 | | enable the AAudio backend. |
612 +----------------------------------+--------------------------------------------------------------------+
613 | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
614 | | enable the OpenSL|ES backend. |
615 +----------------------------------+--------------------------------------------------------------------+
616 | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
617 | | enable the Web Audio backend. |
618 +----------------------------------+--------------------------------------------------------------------+
619 | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
620 | | enable the null backend. |
621 +----------------------------------+--------------------------------------------------------------------+
622 | MA_NO_DECODING | Disables decoding APIs. |
623 +----------------------------------+--------------------------------------------------------------------+
624 | MA_NO_ENCODING | Disables encoding APIs. |
625 +----------------------------------+--------------------------------------------------------------------+
626 | MA_NO_WAV | Disables the built-in WAV decoder and encoder. |
627 +----------------------------------+--------------------------------------------------------------------+
628 | MA_NO_FLAC | Disables the built-in FLAC decoder. |
629 +----------------------------------+--------------------------------------------------------------------+
630 | MA_NO_MP3 | Disables the built-in MP3 decoder. |
631 +----------------------------------+--------------------------------------------------------------------+
632 | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` |
633 | | and `ma_device` APIs. This is useful if you only want to use |
634 | | miniaudio's data conversion and/or decoding APIs. |
635 +----------------------------------+--------------------------------------------------------------------+
636 | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will |
637 | | also disable the following functions: |
638 | | |
639 | | ``` |
640 | | ma_sound_init_from_file() |
641 | | ma_sound_init_from_file_w() |
642 | | ma_sound_init_copy() |
643 | | ma_engine_play_sound_ex() |
644 | | ma_engine_play_sound() |
645 | | ``` |
646 | | |
647 | | The only way to initialize a `ma_sound` object is to initialize it |
648 | | from a data source. |
649 +----------------------------------+--------------------------------------------------------------------+
650 | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API |
651 | | because it depends on the node graph. |
652 +----------------------------------+--------------------------------------------------------------------+
653 | MA_NO_ENGINE | Disables the engine API. |
654 +----------------------------------+--------------------------------------------------------------------+
655 | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and |
656 | | `ma_event` APIs. This option is useful if you only need to use |
657 | | miniaudio for data conversion, decoding and/or encoding. Some |
658 | | families of APIs require threading which means the following |
659 | | options must also be set: |
660 | | |
661 | | ``` |
662 | | MA_NO_DEVICE_IO |
663 | | ``` |
664 +----------------------------------+--------------------------------------------------------------------+
665 | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. |
666 +----------------------------------+--------------------------------------------------------------------+
667 | MA_NO_SSE2 | Disables SSE2 optimizations. |
668 +----------------------------------+--------------------------------------------------------------------+
669 | MA_NO_AVX2 | Disables AVX2 optimizations. |
670 +----------------------------------+--------------------------------------------------------------------+
671 | MA_NO_NEON | Disables NEON optimizations. |
672 +----------------------------------+--------------------------------------------------------------------+
673 | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's |
674 | | notarization process. When enabling this, you may need to avoid |
675 | | using `-std=c89` or `-std=c99` on Linux builds or else you may end |
676 | | up with compilation errors due to conflicts with `timespec` and |
677 | | `timeval` data types. |
678 | | |
679 | | You may need to enable this if your target platform does not allow |
680 | | runtime linking via `dlopen()`. |
681 +----------------------------------+--------------------------------------------------------------------+
682 | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). |
683 +----------------------------------+--------------------------------------------------------------------+
684 | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
685 | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
686 +----------------------------------+--------------------------------------------------------------------+
687 | MA_API | Controls how public APIs should be decorated. Default is `extern`. |
688 +----------------------------------+--------------------------------------------------------------------+
689
690
691 3. Definitions
692 ==============
693 This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity
694 in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio
695 uses each term.
696
697 3.1. Sample
698 -----------
699 A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit
700 floating point number.
701
702 3.2. Frame / PCM Frame
703 ----------------------
704 A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2
705 samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame"
706 and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame.
707 If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always
708 clarify what it's referring to with something like "FLAC frame".
709
710 3.3. Channel
711 ------------
712 A stream of monaural audio that is emitted from an individual speaker in a speaker system, or
713 received from an individual microphone in a microphone system. A stereo stream has two channels (a
714 left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio
715 systems refer to a channel as a complex audio stream that's mixed with other channels to produce
716 the final mix - this is completely different to miniaudio's use of the term "channel" and should
717 not be confused.
718
719 3.4. Sample Rate
720 ----------------
721 The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number
722 of PCM frames that are processed per second.
723
724 3.5. Formats
725 ------------
726 Throughout miniaudio you will see references to different sample formats:
727
728 +---------------+----------------------------------------+---------------------------+
729 | Symbol | Description | Range |
730 +---------------+----------------------------------------+---------------------------+
731 | ma_format_f32 | 32-bit floating point | [-1, 1] |
732 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
733 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
734 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
735 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
736 +---------------+----------------------------------------+---------------------------+
737
738 All formats are native-endian.
739
740
741
742 4. Data Sources
743 ===============
744 The data source abstraction in miniaudio is used for retrieving audio data from some source. A few
745 examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data
746 sources in order to make sense of some of the higher level concepts in miniaudio.
747
748 The `ma_data_source` API is a generic interface for reading from a data source. Any object that
749 implements the data source interface can be plugged into any `ma_data_source` function.
750
751 To read data from a data source:
752
753 ```c
754 ma_result result;
755 ma_uint64 framesRead;
756
757 result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead);
758 if (result != MA_SUCCESS) {
759 return result; // Failed to read data from the data source.
760 }
761 ```
762
763 If you don't need the number of frames that were successfully read you can pass in `NULL` to the
764 `pFramesRead` parameter. If this returns a value less than the number of frames requested it means
765 the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames
766 read is 0.
767
768 When calling any data source function, with the exception of `ma_data_source_init()` and
769 `ma_data_source_uninit()`, you can pass in any object that implements a data source. For example,
770 you could plug in a decoder like so:
771
772 ```c
773 ma_result result;
774 ma_uint64 framesRead;
775 ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`.
776
777 result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead);
778 if (result != MA_SUCCESS) {
779 return result; // Failed to read data from the decoder.
780 }
781 ```
782
783 If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you
784 can use `ma_data_source_seek_pcm_frames()`.
785
786 To seek to a specific PCM frame:
787
788 ```c
789 result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);
790 if (result != MA_SUCCESS) {
791 return result; // Failed to seek to PCM frame.
792 }
793 ```
794
795 You can retrieve the total length of a data source in PCM frames, but note that some data sources
796 may not have the notion of a length, such as noise and waveforms, and others may just not have a
797 way of determining the length such as some decoders. To retrieve the length:
798
799 ```c
800 ma_uint64 length;
801
802 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length);
803 if (result != MA_SUCCESS) {
804 return result; // Failed to retrieve the length.
805 }
806 ```
807
808 Care should be taken when retrieving the length of a data source where the underlying decoder is
809 pulling data from a data stream with an undefined length, such as internet radio or some kind of
810 broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return.
811
812 The current position of the cursor in PCM frames can also be retrieved:
813
814 ```c
815 ma_uint64 cursor;
816
817 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
818 if (result != MA_SUCCESS) {
819 return result; // Failed to retrieve the cursor.
820 }
821 ```
822
823 You will often need to know the data format that will be returned after reading. This can be
824 retrieved like so:
825
826 ```c
827 ma_format format;
828 ma_uint32 channels;
829 ma_uint32 sampleRate;
830 ma_channel channelMap[MA_MAX_CHANNELS];
831
832 result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
833 if (result != MA_SUCCESS) {
834 return result; // Failed to retrieve data format.
835 }
836 ```
837
838 If you do not need a specific data format property, just pass in NULL to the respective parameter.
839
840 There may be cases where you want to implement something like a sound bank where you only want to
841 read data within a certain range of the underlying data. To do this you can use a range:
842
843 ```c
844 result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames);
845 if (result != MA_SUCCESS) {
846 return result; // Failed to set the range.
847 }
848 ```
849
850 This is useful if you have a sound bank where many sounds are stored in the same file and you want
851 the data source to only play one of those sub-sounds. Note that once the range is set, everything
852 that takes a position, such as cursors and loop points, should always be relatvie to the start of
853 the range. When the range is set, any previously defined loop point will be reset.
854
855 Custom loop points can also be used with data sources. By default, data sources will loop after
856 they reach the end of the data source, but if you need to loop at a specific location, you can do
857 the following:
858
859 ```c
860 result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames);
861 if (result != MA_SUCCESS) {
862 return result; // Failed to set the loop point.
863 }
864 ```
865
866 The loop point is relative to the current range.
867
868 It's sometimes useful to chain data sources together so that a seamless transition can be achieved.
869 To do this, you can use chaining:
870
871 ```c
872 ma_decoder decoder1;
873 ma_decoder decoder2;
874
875 // ... initialize decoders with ma_decoder_init_*() ...
876
877 result = ma_data_source_set_next(&decoder1, &decoder2);
878 if (result != MA_SUCCESS) {
879 return result; // Failed to set the next data source.
880 }
881
882 result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead);
883 if (result != MA_SUCCESS) {
884 return result; // Failed to read from the decoder.
885 }
886 ```
887
888 In the example above we're using decoders. When reading from a chain, you always want to read from
889 the top level data source in the chain. In the example above, `decoder1` is the top level data
890 source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
891 gaps.
892
893 Note that when looping is enabled, only the current data source will be looped. You can loop the
894 entire chain by linking in a loop like so:
895
896 ```c
897 ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2
898 ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start).
899 ```
900
901 Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically
902 changing links while the audio thread is in the middle of reading.
903
904 Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
905 instances of the same sound simultaneously. This can be extremely inefficient depending on the type
906 of data source and can result in glitching due to subtle changes to the state of internal filters.
907 Instead, initialize multiple data sources for each instance.
908
909
910 4.1. Custom Data Sources
911 ------------------------
912 You can implement a custom data source by implementing the functions in `ma_data_source_vtable`.
913 Your custom object must have `ma_data_source_base` as it's first member:
914
915 ```c
916 struct my_data_source
917 {
918 ma_data_source_base base;
919 ...
920 };
921 ```
922
923 In your initialization routine, you need to call `ma_data_source_init()` in order to set up the
924 base object (`ma_data_source_base`):
925
926 ```c
927 static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
928 {
929 // Read data here. Output in the same format returned by my_data_source_get_data_format().
930 }
931
932 static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
933 {
934 // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported.
935 }
936
937 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)
938 {
939 // Return the format of the data here.
940 }
941
942 static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
943 {
944 // 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.
945 }
946
947 static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
948 {
949 // 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.
950 }
951
952 static ma_data_source_vtable g_my_data_source_vtable =
953 {
954 my_data_source_read,
955 my_data_source_seek,
956 my_data_source_get_data_format,
957 my_data_source_get_cursor,
958 my_data_source_get_length
959 };
960
961 ma_result my_data_source_init(my_data_source* pMyDataSource)
962 {
963 ma_result result;
964 ma_data_source_config baseConfig;
965
966 baseConfig = ma_data_source_config_init();
967 baseConfig.vtable = &g_my_data_source_vtable;
968
969 result = ma_data_source_init(&baseConfig, &pMyDataSource->base);
970 if (result != MA_SUCCESS) {
971 return result;
972 }
973
974 // ... do the initialization of your custom data source here ...
975
976 return MA_SUCCESS;
977 }
978
979 void my_data_source_uninit(my_data_source* pMyDataSource)
980 {
981 // ... do the uninitialization of your custom data source here ...
982
983 // You must uninitialize the base data source.
984 ma_data_source_uninit(&pMyDataSource->base);
985 }
986 ```
987
988 Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside
989 of the custom data source. It's up to the custom data source itself to call these within their own
990 init/uninit functions.
991
992
993
994 5. Engine
995 =========
996 The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The
997 `ma_engine` object encapsulates a resource manager and a node graph, both of which will be
998 explained in more detail later.
999
1000 Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing
1001 group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and
1002 `ma_sound_group` objects are nodes within the engine's node graph.
1003
1004 When the engine is initialized, it will normally create a device internally. If you would rather
1005 manage the device yourself, you can do so and just pass a pointer to it via the engine config when
1006 you initialize the engine. You can also just use the engine without a device, which again can be
1007 configured via the engine config.
1008
1009 The most basic way to initialize the engine is with a default config, like so:
1010
1011 ```c
1012 ma_result result;
1013 ma_engine engine;
1014
1015 result = ma_engine_init(NULL, &engine);
1016 if (result != MA_SUCCESS) {
1017 return result; // Failed to initialize the engine.
1018 }
1019 ```
1020
1021 This will result in the engine initializing a playback device using the operating system's default
1022 device. This will be sufficient for many use cases, but if you need more flexibility you'll want to
1023 configure the engine with an engine config:
1024
1025 ```c
1026 ma_result result;
1027 ma_engine engine;
1028 ma_engine_config engineConfig;
1029
1030 engineConfig = ma_engine_config_init();
1031 engineConfig.pDevice = &myDevice;
1032
1033 result = ma_engine_init(&engineConfig, &engine);
1034 if (result != MA_SUCCESS) {
1035 return result; // Failed to initialize the engine.
1036 }
1037 ```
1038
1039 In the example above we're passing in a pre-initialized device. Since the caller is the one in
1040 control of the device's data callback, it's their responsibility to manually call
1041 `ma_engine_read_pcm_frames()` from inside their data callback:
1042
1043 ```c
1044 void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
1045 {
1046 ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL);
1047 }
1048 ```
1049
1050 You can also use the engine independent of a device entirely:
1051
1052 ```c
1053 ma_result result;
1054 ma_engine engine;
1055 ma_engine_config engineConfig;
1056
1057 engineConfig = ma_engine_config_init();
1058 engineConfig.noDevice = MA_TRUE;
1059 engineConfig.channels = 2; // Must be set when not using a device.
1060 engineConfig.sampleRate = 48000; // Must be set when not using a device.
1061
1062 result = ma_engine_init(&engineConfig, &engine);
1063 if (result != MA_SUCCESS) {
1064 return result; // Failed to initialize the engine.
1065 }
1066 ```
1067
1068 Note that when you're not using a device, you must set the channel count and sample rate in the
1069 config or else miniaudio won't know what to use (miniaudio will use the device to determine this
1070 normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio
1071 data from the engine. This kind of setup is useful if you want to do something like offline
1072 processing or want to use a different audio system for playback such as SDL.
1073
1074 When a sound is loaded it goes through a resource manager. By default the engine will initialize a
1075 resource manager internally, but you can also specify a pre-initialized resource manager:
1076
1077 ```c
1078 ma_result result;
1079 ma_engine engine1;
1080 ma_engine engine2;
1081 ma_engine_config engineConfig;
1082
1083 engineConfig = ma_engine_config_init();
1084 engineConfig.pResourceManager = &myResourceManager;
1085
1086 ma_engine_init(&engineConfig, &engine1);
1087 ma_engine_init(&engineConfig, &engine2);
1088 ```
1089
1090 In this example we are initializing two engines, both of which are sharing the same resource
1091 manager. This is especially useful for saving memory when loading the same file across multiple
1092 engines. If you were not to use a shared resource manager, each engine instance would use their own
1093 which would result in any sounds that are used between both engine's being loaded twice. By using
1094 a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you
1095 need to output to multiple playback devices, such as in a local multiplayer game where each player
1096 is using their own set of headphones.
1097
1098 By default an engine will be in a started state. To make it so the engine is not automatically
1099 started you can configure it as such:
1100
1101 ```c
1102 engineConfig.noAutoStart = MA_TRUE;
1103
1104 // The engine will need to be started manually.
1105 ma_engine_start(&engine);
1106
1107 // Later on the engine can be stopped with ma_engine_stop().
1108 ma_engine_stop(&engine);
1109 ```
1110
1111 The concept of starting or stopping an engine is only relevant when using the engine with a
1112 device. Attempting to start or stop an engine that is not associated with a device will result in
1113 `MA_INVALID_OPERATION`.
1114
1115 The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a
1116 linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you
1117 prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear.
1118
1119 When a sound is spatialized, it is done so relative to a listener. An engine can be configured to
1120 have multiple listeners which can be configured via the config:
1121
1122 ```c
1123 engineConfig.listenerCount = 2;
1124 ```
1125
1126 The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a
1127 sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound
1128 to a specific listener which will be explained later. Listener's have a position, direction, cone,
1129 and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up
1130 to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The
1131 position, direction and velocity are all specified in absolute terms:
1132
1133 ```c
1134 ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ);
1135 ```
1136
1137 The direction of the listener represents it's forward vector. The listener's up vector can also be
1138 specified and defaults to +1 on the Y axis.
1139
1140 ```c
1141 ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ);
1142 ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0);
1143 ```
1144
1145 The engine supports directional attenuation. The listener can have a cone the controls how sound is
1146 attenuated based on the listener's direction. When a sound is between the inner and outer cones, it
1147 will be attenuated between 1 and the cone's outer gain:
1148
1149 ```c
1150 ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain);
1151 ```
1152
1153 When a sound is inside the inner code, no directional attenuation is applied. When the sound is
1154 outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When
1155 the sound is in between the inner and outer cones, the attenuation will be interpolated between 1
1156 and the outer gain.
1157
1158 The engine's coordinate system follows the OpenGL coordinate system where positive X points right,
1159 positive Y points up and negative Z points forward.
1160
1161 The simplest and least flexible way to play a sound is like so:
1162
1163 ```c
1164 ma_engine_play_sound(&engine, "my_sound.wav", pGroup);
1165 ```
1166
1167 This is a "fire and forget" style of function. The engine will manage the `ma_sound` object
1168 internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility
1169 you'll want to initialize a sound object:
1170
1171 ```c
1172 ma_sound sound;
1173
1174 result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound);
1175 if (result != MA_SUCCESS) {
1176 return result; // Failed to load sound.
1177 }
1178 ```
1179
1180 Sounds need to be uninitialized with `ma_sound_uninit()`.
1181
1182 The example above loads a sound from a file. If the resource manager has been disabled you will not
1183 be able to use this function and instead you'll need to initialize a sound directly from a data
1184 source:
1185
1186 ```c
1187 ma_sound sound;
1188
1189 result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound);
1190 if (result != MA_SUCCESS) {
1191 return result;
1192 }
1193 ```
1194
1195 Each `ma_sound` object represents a single instance of the sound. If you want to play the same
1196 sound multiple times at the same time, you need to initialize a separate `ma_sound` object.
1197
1198 For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's
1199 standard config/init pattern:
1200
1201 ```c
1202 ma_sound sound;
1203 ma_sound_config soundConfig;
1204
1205 soundConfig = ma_sound_config_init();
1206 soundConfig.pFilePath = NULL; // Set this to load from a file path.
1207 soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source.
1208 soundConfig.pInitialAttachment = &someNodeInTheNodeGraph;
1209 soundConfig.initialAttachmentInputBusIndex = 0;
1210 soundConfig.channelsIn = 1;
1211 soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count.
1212
1213 result = ma_sound_init_ex(&soundConfig, &sound);
1214 if (result != MA_SUCCESS) {
1215 return result;
1216 }
1217 ```
1218
1219 In the example above, the sound is being initialized without a file nor a data source. This is
1220 valid, in which case the sound acts as a node in the middle of the node graph. This means you can
1221 connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly
1222 what a `ma_sound_group` is.
1223
1224 When loading a sound, you specify a set of flags that control how the sound is loaded and what
1225 features are enabled for that sound. When no flags are set, the sound will be fully loaded into
1226 memory in exactly the same format as how it's stored on the file system. The resource manager will
1227 allocate a block of memory and then load the file directly into it. When reading audio data, it
1228 will be decoded dynamically on the fly. In order to save processing time on the audio thread, it
1229 might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag:
1230
1231 ```c
1232 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound);
1233 ```
1234
1235 By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
1236 the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
1237 by specifying the `MA_SOUND_FLAG_ASYNC` flag:
1238
1239 ```c
1240 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
1241 ```
1242
1243 This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully
1244 loaded. When you start the sound, it won't output anything until some sound is available. The sound
1245 will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE`
1246 is specified.
1247
1248 If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A
1249 fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal
1250 counter hit's zero. You can specify a fence like so:
1251
1252 ```c
1253 ma_result result;
1254 ma_fence fence;
1255 ma_sound sounds[4];
1256
1257 result = ma_fence_init(&fence);
1258 if (result != MA_SUCCESS) {
1259 return result;
1260 }
1261
1262 // Load some sounds asynchronously.
1263 for (int iSound = 0; iSound < 4; iSound += 1) {
1264 ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]);
1265 }
1266
1267 // ... do some other stuff here in the mean time ...
1268
1269 // Wait for all sounds to finish loading.
1270 ma_fence_wait(&fence);
1271 ```
1272
1273 If loading the entire sound into memory is prohibitive, you can also configure the engine to stream
1274 the audio data:
1275
1276 ```c
1277 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound);
1278 ```
1279
1280 When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work
1281 fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
1282 tracks in games.
1283
1284 When loading a sound from a file path, the engine will reference count the file to prevent it from
1285 being loaded if it's already in memory. When you uninitialize a sound, the reference count will be
1286 decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting
1287 system is not used for streams. The engine will use a 64-bit hash of the file name when comparing
1288 file paths which means there's a small chance you might encounter a name collision. If this is an
1289 issue, you'll need to use a different name for one of the colliding file paths, or just not load
1290 from files and instead load from a data source.
1291
1292 You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this
1293 only works for sounds that were initialized with `ma_sound_init_from_file()` and without the
1294 `MA_SOUND_FLAG_STREAM` flag.
1295
1296 When you initialize a sound, if you specify a sound group the sound will be attached to that group
1297 automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
1298 If you would instead rather leave the sound unattached by default, you can can specify the
1299 `MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node
1300 graph.
1301
1302 Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with
1303 `ma_sound_stop()`.
1304
1305 Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the
1306 engine's master volume.
1307
1308 Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan
1309 to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas
1310 +1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger
1311 value will result in a higher pitch. The pitch must be greater than 0.
1312
1313 The engine supports 3D spatialization of sounds. By default sounds will have spatialization
1314 enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways
1315 to disable spatialization of a sound:
1316
1317 ```c
1318 // Disable spatialization at initialization time via a flag:
1319 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound);
1320
1321 // Dynamically disable or enable spatialization post-initialization:
1322 ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled);
1323 ```
1324
1325 By default sounds will be spatialized based on the closest listener. If a sound should always be
1326 spatialized relative to a specific listener it can be pinned to one:
1327
1328 ```c
1329 ma_sound_set_pinned_listener_index(&sound, listenerIndex);
1330 ```
1331
1332 Like listeners, sounds have a position. By default, the position of a sound is in absolute space,
1333 but it can be changed to be relative to a listener:
1334
1335 ```c
1336 ma_sound_set_positioning(&sound, ma_positioning_relative);
1337 ```
1338
1339 Note that relative positioning of a sound only makes sense if there is either only one listener, or
1340 the sound is pinned to a specific listener. To set the position of a sound:
1341
1342 ```c
1343 ma_sound_set_position(&sound, posX, posY, posZ);
1344 ```
1345
1346 The direction works the same way as a listener and represents the sound's forward direction:
1347
1348 ```c
1349 ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ);
1350 ```
1351
1352 Sound's also have a cone for controlling directional attenuation. This works exactly the same as
1353 listeners:
1354
1355 ```c
1356 ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain);
1357 ```
1358
1359 The velocity of a sound is used for doppler effect and can be set as such:
1360
1361 ```c
1362 ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ);
1363 ```
1364
1365 The engine supports different attenuation models which can be configured on a per-sound basis. By
1366 default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to
1367 OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so:
1368
1369 ```c
1370 ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse);
1371 ```
1372
1373 The supported attenuation models include the following:
1374
1375 +----------------------------------+----------------------------------------------+
1376 | ma_attenuation_model_none | No distance attenuation. |
1377 +----------------------------------+----------------------------------------------+
1378 | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. |
1379 +----------------------------------+----------------------------------------------+
1380 | ma_attenuation_model_linear | Linear attenuation. |
1381 +----------------------------------+----------------------------------------------+
1382 | ma_attenuation_model_exponential | Exponential attenuation. |
1383 +----------------------------------+----------------------------------------------+
1384
1385 To control how quickly a sound rolls off as it moves away from the listener, you need to configure
1386 the rolloff:
1387
1388 ```c
1389 ma_sound_set_rolloff(&sound, rolloff);
1390 ```
1391
1392 You can control the minimum and maximum gain to apply from spatialization:
1393
1394 ```c
1395 ma_sound_set_min_gain(&sound, minGain);
1396 ma_sound_set_max_gain(&sound, maxGain);
1397 ```
1398
1399 Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for
1400 the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain
1401 volume after the listener moves further away and to have sounds play a maximum volume when the
1402 listener is within a certain distance:
1403
1404 ```c
1405 ma_sound_set_min_distance(&sound, minDistance);
1406 ma_sound_set_max_distance(&sound, maxDistance);
1407 ```
1408
1409 The engine's spatialization system supports doppler effect. The doppler factor can be configure on
1410 a per-sound basis like so:
1411
1412 ```c
1413 ma_sound_set_doppler_factor(&sound, dopplerFactor);
1414 ```
1415
1416 You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and
1417 `ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the
1418 starting volume:
1419
1420 ```c
1421 // Fade in over 1 second.
1422 ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000);
1423
1424 // ... sometime later ...
1425
1426 // Fade out over 1 second, starting from the current volume.
1427 ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000);
1428 ```
1429
1430 By default sounds will start immediately, but sometimes for timing and synchronization purposes it
1431 can be useful to schedule a sound to start or stop:
1432
1433 ```c
1434 // Start the sound in 1 second from now.
1435 ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
1436
1437 // Stop the sound in 2 seconds from now.
1438 ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
1439 ```
1440
1441 Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before
1442 anything will play.
1443
1444 The time is specified in global time which is controlled by the engine. You can get the engine's
1445 current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented
1446 automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()`
1447 in case it needs to be resynchronized for some reason.
1448
1449 To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will
1450 take the scheduled start and stop times into account.
1451
1452 Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not
1453 be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.
1454
1455 Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping
1456 sound this should never return true. Alternatively, you can configure a callback that will be fired
1457 when the sound reaches the end. Note that the callback is fired from the audio thread which means
1458 you cannot be uninitializing sound from the callback. To set the callback you can use
1459 `ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it
1460 into the config like so:
1461
1462 ```c
1463 soundConfig.endCallback = my_end_callback;
1464 soundConfig.pEndCallbackUserData = pMyEndCallbackUserData;
1465 ```
1466
1467 The end callback is declared like so:
1468
1469 ```c
1470 void my_end_callback(void* pUserData, ma_sound* pSound)
1471 {
1472 ...
1473 }
1474 ```
1475
1476 Internally a sound wraps around a data source. Some APIs exist to control the underlying data
1477 source, mainly for convenience:
1478
1479 ```c
1480 ma_sound_seek_to_pcm_frame(&sound, frameIndex);
1481 ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity);
1482 ma_sound_get_cursor_in_pcm_frames(&sound, &cursor);
1483 ma_sound_get_length_in_pcm_frames(&sound, &length);
1484 ```
1485
1486 Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do
1487 not have any notion of a data source, anything relating to a data source is unavailable.
1488
1489 Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports
1490 file formats that have built-in support in miniaudio. You can extend this to support any kind of
1491 file format through the use of custom decoders. To do this you'll need to use a self-managed
1492 resource manager and configure it appropriately. See the "Resource Management" section below for
1493 details on how to set this up.
1494
1495
1496 6. Resource Management
1497 ======================
1498 Many programs will want to manage sound resources for things such as reference counting and
1499 streaming. This is supported by miniaudio via the `ma_resource_manager` API.
1500
1501 The resource manager is mainly responsible for the following:
1502
1503 * Loading of sound files into memory with reference counting.
1504 * Streaming of sound data.
1505
1506 When loading a sound file, the resource manager will give you back a `ma_data_source` compatible
1507 object called `ma_resource_manager_data_source`. This object can be passed into any
1508 `ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you
1509 specify whether or not you want the sound to be fully loaded into memory (and optionally
1510 pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want
1511 the data to be loaded asynchronously.
1512
1513 The example below is how you can initialize a resource manager using it's default configuration:
1514
1515 ```c
1516 ma_resource_manager_config config;
1517 ma_resource_manager resourceManager;
1518
1519 config = ma_resource_manager_config_init();
1520 result = ma_resource_manager_init(&config, &resourceManager);
1521 if (result != MA_SUCCESS) {
1522 ma_device_uninit(&device);
1523 printf("Failed to initialize the resource manager.");
1524 return -1;
1525 }
1526 ```
1527
1528 You can configure the format, channels and sample rate of the decoded audio data. By default it
1529 will use the file's native data format, but you can configure it to use a consistent format. This
1530 is useful for offloading the cost of data conversion to load time rather than dynamically
1531 converting at mixing time. To do this, you configure the decoded format, channels and sample rate
1532 like the code below:
1533
1534 ```c
1535 config = ma_resource_manager_config_init();
1536 config.decodedFormat = device.playback.format;
1537 config.decodedChannels = device.playback.channels;
1538 config.decodedSampleRate = device.sampleRate;
1539 ```
1540
1541 In the code above, the resource manager will be configured so that any decoded audio data will be
1542 pre-converted at load time to the device's native data format. If instead you used defaults and
1543 the data format of the file did not match the device's data format, you would need to convert the
1544 data at mixing time which may be prohibitive in high-performance and large scale scenarios like
1545 games.
1546
1547 Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it
1548 only supports decoders that are built into miniaudio. It's possible to support additional encoding
1549 formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable`
1550 vtables into the resource manager config:
1551
1552 ```c
1553 ma_decoding_backend_vtable* pCustomBackendVTables[] =
1554 {
1555 &g_ma_decoding_backend_vtable_libvorbis,
1556 &g_ma_decoding_backend_vtable_libopus
1557 };
1558
1559 ...
1560
1561 resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
1562 resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
1563 resourceManagerConfig.pCustomDecodingBackendUserData = NULL;
1564 ```
1565
1566 This system can allow you to support any kind of file format. See the "Decoding" section for
1567 details on how to implement custom decoders. The miniaudio repository includes examples for Opus
1568 via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile.
1569
1570 Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the
1571 decoding of a page, a job will be posted to a queue which will then be processed by a job thread.
1572 By default there will be only one job thread running, but this can be configured, like so:
1573
1574 ```c
1575 config = ma_resource_manager_config_init();
1576 config.jobThreadCount = MY_JOB_THREAD_COUNT;
1577 ```
1578
1579 By default job threads are managed internally by the resource manager, however you can also self
1580 manage your job threads if, for example, you want to integrate the job processing into your
1581 existing job infrastructure, or if you simply don't like the way the resource manager does it. To
1582 do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first
1583 need to retrieve a job using `ma_resource_manager_next_job()` and then process it using
1584 `ma_job_process()`:
1585
1586 ```c
1587 config = ma_resource_manager_config_init();
1588 config.jobThreadCount = 0; // Don't manage any job threads internally.
1589 config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking.
1590
1591 // ... Initialize your custom job threads ...
1592
1593 void my_custom_job_thread(...)
1594 {
1595 for (;;) {
1596 ma_job job;
1597 ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
1598 if (result != MA_SUCCESS) {
1599 if (result == MA_NO_DATA_AVAILABLE) {
1600 // No jobs are available. Keep going. Will only get this if the resource manager was initialized
1601 // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
1602 continue;
1603 } else if (result == MA_CANCELLED) {
1604 // MA_JOB_TYPE_QUIT was posted. Exit.
1605 break;
1606 } else {
1607 // Some other error occurred.
1608 break;
1609 }
1610 }
1611
1612 ma_job_process(&job);
1613 }
1614 }
1615 ```
1616
1617 In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination
1618 indicator, but you can use whatever you would like to terminate the thread. The call to
1619 `ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking
1620 by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration
1621 flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This
1622 is to give every thread the opportunity to catch the event and terminate naturally.
1623
1624 When loading a file, it's sometimes convenient to be able to customize how files are opened and
1625 read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by
1626 default. This can be done by setting `pVFS` member of the resource manager's config:
1627
1628 ```c
1629 // Initialize your custom VFS object. See documentation for VFS for information on how to do this.
1630 my_custom_vfs vfs = my_custom_vfs_init();
1631
1632 config = ma_resource_manager_config_init();
1633 config.pVFS = &vfs;
1634 ```
1635
1636 This is particularly useful in programs like games where you want to read straight from an archive
1637 rather than the normal file system. If you do not specify a custom VFS, the resource manager will
1638 use the operating system's normal file operations.
1639
1640 To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
1641 loading a sound you need to specify the file path and options for how the sounds should be loaded.
1642 By default a sound will be loaded synchronously. The returned data source is owned by the caller
1643 which means the caller is responsible for the allocation and freeing of the data source. Below is
1644 an example for initializing a data source:
1645
1646 ```c
1647 ma_resource_manager_data_source dataSource;
1648 ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource);
1649 if (result != MA_SUCCESS) {
1650 // Error.
1651 }
1652
1653 // ...
1654
1655 // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call
1656 // the `ma_data_source_read_pcm_frames()` like you would with any normal data source.
1657 result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead);
1658 if (result != MA_SUCCESS) {
1659 // Failed to read PCM frames.
1660 }
1661
1662 // ...
1663
1664 ma_resource_manager_data_source_uninit(pResourceManager, &dataSource);
1665 ```
1666
1667 The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
1668 combination of the following flags:
1669
1670 ```
1671 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM
1672 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
1673 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
1674 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
1675 ```
1676
1677 When no flags are specified (set to 0), the sound will be fully loaded into memory, but not
1678 decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when
1679 `ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in
1680 memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will
1681 be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after
1682 the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You
1683 can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag.
1684 This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be
1685 returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is
1686 available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by
1687 `ma_data_source_read_pcm_frames()`.
1688
1689 For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you
1690 can instead stream audio data which you can do by specifying the
1691 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1
1692 second pages. When a new page needs to be decoded, a job will be posted to the job queue and then
1693 subsequently processed in a job thread.
1694
1695 For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means
1696 multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in
1697 the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be
1698 matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful
1699 for a program to register self-managed raw audio data and associate it with a file path. Use the
1700 `ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this.
1701 `ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed
1702 decoded audio data in the specified data format with the specified name. Likewise,
1703 `ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed
1704 encoded audio data (the raw file data) with the specified name. Note that these names need not be
1705 actual file paths. When `ma_resource_manager_data_source_init()` is called (without the
1706 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these
1707 explicitly registered data buffers and, if found, will use it as the backing data for the data
1708 source. Note that the resource manager does *not* make a copy of this data so it is up to the
1709 caller to ensure the pointer stays valid for it's lifetime. Use
1710 `ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use
1711 `ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and
1712 unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM`
1713 flag with a self-managed data pointer.
1714
1715
1716 6.1. Asynchronous Loading and Synchronization
1717 ---------------------------------------------
1718 When loading asynchronously, it can be useful to poll whether or not loading has finished. Use
1719 `ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will
1720 return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded,
1721 `MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed
1722 to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been
1723 decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY`
1724 will be returned. Otherwise, some other error code will be returned if the sound failed to load.
1725
1726 In addition to polling, you can also use a simple synchronization object called a "fence" to wait
1727 for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a
1728 fence is that it can be used to wait for a group of sounds to finish loading rather than waiting
1729 for sounds on an individual basis. There are two stages to loading a sound:
1730
1731 * Initialization of the internal decoder; and
1732 * Completion of decoding of the file (the file is fully decoded)
1733
1734 You can specify separate fences for each of the different stages. Waiting for the initialization
1735 of the internal decoder is important for when you need to know the sample format, channels and
1736 sample rate of the file.
1737
1738 The example below shows how you could use a fence when loading a number of sounds:
1739
1740 ```c
1741 // This fence will be released when all sounds are finished loading entirely.
1742 ma_fence fence;
1743 ma_fence_init(&fence);
1744
1745 // This will be passed into the initialization routine for each sound.
1746 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
1747 notifications.done.pFence = &fence;
1748
1749 // Now load a bunch of sounds:
1750 for (iSound = 0; iSound < soundCount; iSound += 1) {
1751 ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, &notifications, &pSoundSources[iSound]);
1752 }
1753
1754 // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ...
1755
1756 // Wait for loading of sounds to finish.
1757 ma_fence_wait(&fence);
1758 ```
1759
1760 In the example above we used a fence for waiting until the entire file has been fully decoded. If
1761 you only need to wait for the initialization of the internal decoder to complete, you can use the
1762 `init` member of the `ma_resource_manager_pipeline_notifications` object:
1763
1764 ```c
1765 notifications.init.pFence = &fence;
1766 ```
1767
1768 If a fence is not appropriate for your situation, you can instead use a callback that is fired on
1769 an individual sound basis. This is done in a very similar way to fences:
1770
1771 ```c
1772 typedef struct
1773 {
1774 ma_async_notification_callbacks cb;
1775 void* pMyData;
1776 } my_notification;
1777
1778 void my_notification_callback(ma_async_notification* pNotification)
1779 {
1780 my_notification* pMyNotification = (my_notification*)pNotification;
1781
1782 // Do something in response to the sound finishing loading.
1783 }
1784
1785 ...
1786
1787 my_notification myCallback;
1788 myCallback.cb.onSignal = my_notification_callback;
1789 myCallback.pMyData = pMyData;
1790
1791 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
1792 notifications.done.pNotification = &myCallback;
1793
1794 ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, &notifications, &mySound);
1795 ```
1796
1797 In the example above we just extend the `ma_async_notification_callbacks` object and pass an
1798 instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with
1799 the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same
1800 time and they should both work as expected. If using the `pNotification` system, you need to ensure
1801 your `ma_async_notification_callbacks` object stays valid.
1802
1803
1804
1805 6.2. Resource Manager Implementation Details
1806 --------------------------------------------
1807 Resources are managed in two main ways:
1808
1809 * By storing the entire sound inside an in-memory buffer (referred to as a data buffer)
1810 * By streaming audio data on the fly (referred to as a data stream)
1811
1812 A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or
1813 data stream, depending on whether or not the data source was initialized with the
1814 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a
1815 `ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer`
1816 object. Both of these objects are data sources which means they can be used with any
1817 `ma_data_source_*()` API.
1818
1819 Another major feature of the resource manager is the ability to asynchronously decode audio files.
1820 This relieves the audio thread of time-consuming decoding which can negatively affect scalability
1821 due to the audio thread needing to complete it's work extremely quickly to avoid glitching.
1822 Asynchronous decoding is achieved through a job system. There is a central multi-producer,
1823 multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is
1824 posted to the queue which is then read by a job thread. The number of job threads can be
1825 configured for improved scalability, and job threads can all run in parallel without needing to
1826 worry about the order of execution (how this is achieved is explained below).
1827
1828 When a sound is being loaded asynchronously, playback can begin before the sound has been fully
1829 decoded. This enables the application to start playback of the sound quickly, while at the same
1830 time allowing to resource manager to keep loading in the background. Since there may be less
1831 threads than the number of sounds being loaded at a given time, a simple scheduling system is used
1832 to keep decoding time balanced and fair. The resource manager solves this by splitting decoding
1833 into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a
1834 new job will be posted to start decoding the next page. By dividing up decoding into pages, an
1835 individual sound shouldn't ever delay every other sound from having their first page decoded. Of
1836 course, when loading many sounds at the same time, there will always be an amount of time required
1837 to process jobs in the queue so in heavy load situations there will still be some delay. To
1838 determine if a data source is ready to have some frames read, use
1839 `ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames
1840 available starting from the current position.
1841
1842
1843 6.2.1. Job Queue
1844 ----------------
1845 The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity.
1846 This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety.
1847 Only a fixed number of jobs can be allocated and inserted into the queue which is done through a
1848 lock-free data structure for allocating an index into a fixed sized array, with reference counting
1849 for mitigation of the ABA problem. The reference count is 32-bit.
1850
1851 For many types of jobs it's important that they execute in a specific order. In these cases, jobs
1852 are executed serially. For the resource manager, serial execution of jobs is only required on a
1853 per-object basis (per data buffer or per data stream). Each of these objects stores an execution
1854 counter. When a job is posted it is associated with an execution counter. When the job is
1855 processed, it checks if the execution counter of the job equals the execution counter of the
1856 owning object and if so, processes the job. If the counters are not equal, the job will be posted
1857 back onto the job queue for later processing. When the job finishes processing the execution order
1858 of the main object is incremented. This system means the no matter how many job threads are
1859 executing, decoding of an individual sound will always get processed serially. The advantage to
1860 having multiple threads comes into play when loading multiple sounds at the same time.
1861
1862 The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve
1863 thread-safety for a very small section of code. This is only relevant when the resource manager
1864 uses more than one job thread. If only using a single job thread, which is the default, the
1865 lock should never actually wait in practice. The amount of time spent locking should be quite
1866 short, but it's something to be aware of for those who have pedantic lock-free requirements and
1867 need to use more than one job thread. There are plans to remove this lock in a future version.
1868
1869 In addition, posting a job will release a semaphore, which on Win32 is implemented with
1870 `ReleaseSemaphore` and on POSIX platforms via a condition variable:
1871
1872 ```c
1873 pthread_mutex_lock(&pSemaphore->lock);
1874 {
1875 pSemaphore->value += 1;
1876 pthread_cond_signal(&pSemaphore->cond);
1877 }
1878 pthread_mutex_unlock(&pSemaphore->lock);
1879 ```
1880
1881 Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid
1882 this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING`
1883 flag) and implement your own job processing routine (see the "Resource Manager" section above for
1884 details on how to do this).
1885
1886
1887
1888 6.2.2. Data Buffers
1889 -------------------
1890 When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the
1891 resource manager will try to load the data into an in-memory data buffer. Before doing so, however,
1892 it will first check if the specified file is already loaded. If so, it will increment a reference
1893 counter and just use the already loaded data. This saves both time and memory. When the data buffer
1894 is uninitialized, the reference counter will be decremented. If the counter hits zero, the file
1895 will be unloaded. This is a detail to keep in mind because it could result in excessive loading and
1896 unloading of a sound. For example, the following sequence will result in a file be loaded twice,
1897 once after the other:
1898
1899 ```c
1900 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
1901 ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded.
1902
1903 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
1904 ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded.
1905 ```
1906
1907 A binary search tree (BST) is used for storing data buffers as it has good balance between
1908 efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
1909 into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
1910 memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
1911 due to the random nature of the hash. The disadvantages are that file names are case-sensitive and
1912 there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize
1913 your file names to upper- or lower-case before initializing your data sources. If name collisions
1914 become an issue, you'll need to change the name of one of the colliding names or just not use the
1915 resource manager.
1916
1917 When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
1918 flag is excluded, the file will be decoded synchronously by the calling thread. There are two
1919 options for controlling how the audio is stored in the data buffer - encoded or decoded. When the
1920 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored
1921 in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is
1922 a very simple and standard process of simply adding an item to the BST, allocating a block of
1923 memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified).
1924
1925 When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer
1926 is done asynchronously. In this case, a job is posted to the queue to start loading and then the
1927 function immediately returns, setting an internal result code to `MA_BUSY`. This result code is
1928 returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully
1929 completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed.
1930
1931 When loading asynchronously, a single job is posted to the queue of the type
1932 `MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and
1933 associating it with job. When the job is processed by the job thread, it will first load the file
1934 using the VFS associated with the resource manager. When using a custom VFS, it's important that it
1935 be completely thread-safe because it will be used from one or more job threads at the same time.
1936 Individual files should only ever be accessed by one thread at a time, however. After opening the
1937 file via the VFS, the job will determine whether or not the file is being decoded. If not, it
1938 simply allocates a block of memory and loads the raw file contents into it and returns. On the
1939 other hand, when the file is being decoded, it will first allocate a decoder on the heap and
1940 initialize it. Then it will check if the length of the file is known. If so it will allocate a
1941 block of memory to store the decoded output and initialize it to silence. If the size is unknown,
1942 it will allocate room for one page. After memory has been allocated, the first page will be
1943 decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the
1944 completion event will be signalled and loading is now complete. If, however, there is more to
1945 decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job
1946 will decode the next page and perform the same process if it reaches the end. If there is more to
1947 decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will
1948 keep on happening until the sound has been fully decoded. For sounds of an unknown length, each
1949 page will be linked together as a linked list. Internally this is implemented via the
1950 `ma_paged_audio_buffer` object.
1951
1952
1953 6.2.3. Data Streams
1954 -------------------
1955 Data streams only ever store two pages worth of data for each instance. They are most useful for
1956 large sounds like music tracks in games that would consume too much memory if fully decoded in
1957 memory. After every frame from a page has been read, a job will be posted to load the next page
1958 which is done from the VFS.
1959
1960 For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or
1961 not initialization of the data source waits until the two pages have been decoded. When unset,
1962 `ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise
1963 it will return immediately.
1964
1965 When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`,
1966 `MA_BUSY` will be returned if there are no frames available. If there are some frames available,
1967 but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames
1968 read will be less than the number requested. Due to the asynchronous nature of data streams,
1969 seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be
1970 returned when trying to read frames.
1971
1972 When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed
1973 a job is posted to load the next page. This will be posted from the same thread that called
1974 `ma_resource_manager_data_source_read_pcm_frames()`.
1975
1976 Data streams are uninitialized by posting a job to the queue, but the function won't return until
1977 that job has been processed. The reason for this is that the caller owns the data stream object and
1978 therefore miniaudio needs to ensure everything completes before handing back control to the caller.
1979 Also, if the data stream is uninitialized while pages are in the middle of decoding, they must
1980 complete before destroying any underlying object and the job system handles this cleanly.
1981
1982 Note that when a new page needs to be loaded, a job will be posted to the resource manager's job
1983 thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue"
1984 section above regarding locking when posting an event if you require a strictly lock-free audio
1985 thread.
1986
1987
1988
1989 7. Node Graph
1990 =============
1991 miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a
1992 node whose outputs are attached to inputs of another node, thereby creating a graph. There are
1993 different types of nodes, with each node in the graph processing input data to produce output,
1994 which is then fed through the chain. Each node in the graph can apply their own custom effects. At
1995 the start of the graph will usually be one or more data source nodes which have no inputs and
1996 instead pull their data from a data source. At the end of the graph is an endpoint which represents
1997 the end of the chain and is where the final output is ultimately extracted from.
1998
1999 Each node has a number of input buses and a number of output buses. An output bus from a node is
2000 attached to an input bus of another. Multiple nodes can connect their output buses to another
2001 node's input bus, in which case their outputs will be mixed before processing by the node. Below is
2002 a diagram that illustrates a hypothetical node graph setup:
2003
2004 ```
2005 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
2006
2007 +---------------+ +-----------------+
2008 | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+
2009 +---------------+ | | =----+ +-----------------+ | +----------+
2010 +----= Splitter | +----= ENDPOINT |
2011 +---------------+ | | =----+ +-----------------+ | +----------+
2012 | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+
2013 +---------------+ +-----------------+
2014 ```
2015
2016 In the above graph, it starts with two data sources whose outputs are attached to the input of a
2017 splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter
2018 performs it's processing routine and produces two outputs which is simply a duplication of the
2019 input stream. One output is attached to a low pass filter, whereas the other output is attached to
2020 a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
2021 since they're both connected to the same input bus, they'll be mixed.
2022
2023 Each input bus must be configured to accept the same number of channels, but the number of channels
2024 used by input buses can be different to the number of channels for output buses in which case
2025 miniaudio will automatically convert the input data to the output channel count before processing.
2026 The number of channels of an output bus of one node must match the channel count of the input bus
2027 it's attached to. The channel counts cannot be changed after the node has been initialized. If you
2028 attempt to attach an output bus to an input bus with a different channel count, attachment will
2029 fail.
2030
2031 To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a
2032 container around the entire graph. The `ma_node_graph` object is required for some thread-safety
2033 issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's
2034 standard config/init system:
2035
2036 ```c
2037 ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount);
2038
2039 result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks.
2040 if (result != MA_SUCCESS) {
2041 // Failed to initialize node graph.
2042 }
2043 ```
2044
2045 When you initialize the node graph, you're specifying the channel count of the endpoint. The
2046 endpoint is a special node which has one input bus and one output bus, both of which have the
2047 same channel count, which is specified in the config. Any nodes that connect directly to the
2048 endpoint must be configured such that their output buses have the same channel count. When you read
2049 audio data from the node graph, it'll have the channel count you specified in the config. To read
2050 data from the graph:
2051
2052 ```c
2053 ma_uint32 framesRead;
2054 result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead);
2055 if (result != MA_SUCCESS) {
2056 // Failed to read data from the node graph.
2057 }
2058 ```
2059
2060 When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
2061 data from it's input attachments, which in turn recursively pull in data from their inputs, and so
2062 on. At the start of the graph there will be some kind of data source node which will have zero
2063 inputs and will instead read directly from a data source. The base nodes don't literally need to
2064 read from a `ma_data_source` object, but they will always have some kind of underlying object that
2065 sources some kind of audio. The `ma_data_source_node` node can be used to read from a
2066 `ma_data_source`. Data is always in floating-point format and in the number of channels you
2067 specified when the graph was initialized. The sample rate is defined by the underlying data sources.
2068 It's up to you to ensure they use a consistent and appropriate sample rate.
2069
2070 The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
2071 miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
2072 node which reads directly from a data source (`ma_data_source_node`) which is an example of one
2073 of the stock nodes that comes with miniaudio:
2074
2075 ```c
2076 ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource);
2077
2078 ma_data_source_node dataSourceNode;
2079 result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode);
2080 if (result != MA_SUCCESS) {
2081 // Failed to create data source node.
2082 }
2083 ```
2084
2085 The data source node will use the output channel count to determine the channel count of the output
2086 bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data
2087 source). The data source must output to floating-point (`ma_format_f32`) or else an error will be
2088 returned from `ma_data_source_node_init()`.
2089
2090 By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`:
2091
2092 ```c
2093 result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0);
2094 if (result != MA_SUCCESS) {
2095 // Failed to attach node.
2096 }
2097 ```
2098
2099 The code above connects the data source node directly to the endpoint. Since the data source node
2100 has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single
2101 input bus which means the input bus index will also always be 0.
2102
2103 To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use
2104 `ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to
2105 another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll
2106 deal with it for you.
2107
2108 Less frequently you may want to create a specialized node. This will be a node where you implement
2109 your own processing callback to apply a custom effect of some kind. This is similar to initializing
2110 one of the stock node types, only this time you need to specify a pointer to a vtable containing a
2111 pointer to the processing function and the number of input and output buses. Example:
2112
2113 ```c
2114 static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
2115 {
2116 // Do some processing of ppFramesIn (one stream of audio data per input bus)
2117 const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0.
2118 const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1.
2119 float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0.
2120
2121 // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each
2122 // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers
2123 // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames
2124 // your node consumed and `pFrameCountOut` should be set the number of output frames that
2125 // were produced.
2126 //
2127 // You should process as many frames as you can. If your effect consumes input frames at the
2128 // same rate as output frames (always the case, unless you're doing resampling), you need
2129 // only look at `ppFramesOut` and process that exact number of frames. If you're doing
2130 // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut`
2131 // properly.
2132 }
2133
2134 static ma_node_vtable my_custom_node_vtable =
2135 {
2136 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.
2137 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
2138 2, // 2 input buses.
2139 1, // 1 output bus.
2140 0 // Default flags.
2141 };
2142
2143 ...
2144
2145 // Each bus needs to have a channel count specified. To do this you need to specify the channel
2146 // counts in an array and then pass that into the node config.
2147 ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable.
2148 ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable.
2149
2150 inputChannels[0] = channelsIn;
2151 inputChannels[1] = channelsIn;
2152 outputChannels[0] = channelsOut;
2153
2154 ma_node_config nodeConfig = ma_node_config_init();
2155 nodeConfig.vtable = &my_custom_node_vtable;
2156 nodeConfig.pInputChannels = inputChannels;
2157 nodeConfig.pOutputChannels = outputChannels;
2158
2159 ma_node_base node;
2160 result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node);
2161 if (result != MA_SUCCESS) {
2162 // Failed to initialize node.
2163 }
2164 ```
2165
2166 When initializing a custom node, as in the code above, you'll normally just place your vtable in
2167 static space. The number of input and output buses are specified as part of the vtable. If you need
2168 a variable number of buses on a per-node bases, the vtable should have the relevant bus count set
2169 to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config:
2170
2171 ```c
2172 static ma_node_vtable my_custom_node_vtable =
2173 {
2174 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.
2175 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
2176 MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis.
2177 1, // 1 output bus.
2178 0 // Default flags.
2179 };
2180
2181 ...
2182
2183 ma_node_config nodeConfig = ma_node_config_init();
2184 nodeConfig.vtable = &my_custom_node_vtable;
2185 nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here.
2186 nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array.
2187 nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array.
2188 ```
2189
2190 In the above example it's important to never set the `inputBusCount` and `outputBusCount` members
2191 to anything other than their defaults if the vtable specifies an explicit count. They can only be
2192 set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count.
2193
2194 Most often you'll want to create a structure to encapsulate your node with some extra data. You
2195 need to make sure the `ma_node_base` object is your first member of the structure:
2196
2197 ```c
2198 typedef struct
2199 {
2200 ma_node_base base; // <-- Make sure this is always the first member.
2201 float someCustomData;
2202 } my_custom_node;
2203 ```
2204
2205 By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the
2206 graph just like any other node.
2207
2208 In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the
2209 number of channels for each bus is what was specified by the config when the node was initialized
2210 with `ma_node_init()`. In addition, all attachments to each of the input buses will have been
2211 pre-mixed by miniaudio. The config allows you to specify different channel counts for each
2212 individual input and output bus. It's up to the effect to handle it appropriate, and if it can't,
2213 return an error in it's initialization routine.
2214
2215 Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable
2216 and include the following:
2217
2218 +-----------------------------------------+---------------------------------------------------+
2219 | Flag Name | Description |
2220 +-----------------------------------------+---------------------------------------------------+
2221 | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio |
2222 | | processing, but are instead used for tracking |
2223 | | time, handling events, etc. Also used by the |
2224 | | internal endpoint node. It reads directly from |
2225 | | the input bus to the output bus. Nodes with this |
2226 | | flag must have exactly 1 input bus and 1 output |
2227 | | bus, and both buses must have the same channel |
2228 | | counts. |
2229 +-----------------------------------------+---------------------------------------------------+
2230 | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even |
2231 | | when no data is available to be read from input |
2232 | | attachments. When a node has at least one input |
2233 | | bus, but there are no inputs attached or the |
2234 | | inputs do not deliver any data, the node's |
2235 | | processing callback will not get fired. This flag |
2236 | | will make it so the callback is always fired |
2237 | | regardless of whether or not any input data is |
2238 | | received. This is useful for effects like |
2239 | | echos where there will be a tail of audio data |
2240 | | that still needs to be processed even when the |
2241 | | original data sources have reached their ends. It |
2242 | | may also be useful for nodes that must always |
2243 | | have their processing callback fired when there |
2244 | | are no inputs attached. |
2245 +-----------------------------------------+---------------------------------------------------+
2246 | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with |
2247 | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this |
2248 | | is set, the `ppFramesIn` parameter of the |
2249 | | processing callback will be set to NULL when |
2250 | | there are no input frames are available. When |
2251 | | this is unset, silence will be posted to the |
2252 | | processing callback. |
2253 +-----------------------------------------+---------------------------------------------------+
2254 | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output |
2255 | | frames are processed at different rates. You |
2256 | | should set this for any nodes that perform |
2257 | | resampling. |
2258 +-----------------------------------------+---------------------------------------------------+
2259 | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only |
2260 | | silent output. This is useful for nodes where you |
2261 | | don't want the output to contribute to the final |
2262 | | mix. An example might be if you want split your |
2263 | | stream and have one branch be output to a file. |
2264 | | When using this flag, you should avoid writing to |
2265 | | the output buffer of the node's processing |
2266 | | callback because miniaudio will ignore it anyway. |
2267 +-----------------------------------------+---------------------------------------------------+
2268
2269
2270 If you need to make a copy of an audio stream for effect processing you can use a splitter node
2271 called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses.
2272 You can use it like this:
2273
2274 ```c
2275 ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels);
2276
2277 ma_splitter_node splitterNode;
2278 result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
2279 if (result != MA_SUCCESS) {
2280 // Failed to create node.
2281 }
2282
2283 // Attach your output buses to two different input buses (can be on two different nodes).
2284 ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint.
2285 ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node.
2286 ```
2287
2288 The volume of an output bus can be configured on a per-bus basis:
2289
2290 ```c
2291 ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);
2292 ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);
2293 ```
2294
2295 In the code above we're using the splitter node from before and changing the volume of each of the
2296 copied streams.
2297
2298 You can start and stop a node with the following:
2299
2300 ```c
2301 ma_node_set_state(&splitterNode, ma_node_state_started); // The default state.
2302 ma_node_set_state(&splitterNode, ma_node_state_stopped);
2303 ```
2304
2305 By default the node is in a started state, but since it won't be connected to anything won't
2306 actually be invoked by the node graph until it's connected. When you stop a node, data will not be
2307 read from any of it's input connections. You can use this property to stop a group of sounds
2308 atomically.
2309
2310 You can configure the initial state of a node in it's config:
2311
2312 ```c
2313 nodeConfig.initialState = ma_node_state_stopped;
2314 ```
2315
2316 Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member
2317 which is the config to use with the base node. This is where the initial state can be configured
2318 for specialized nodes:
2319
2320 ```c
2321 dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped;
2322 ```
2323
2324 When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not
2325 modify the `vtable` member of the `nodeConfig` object.
2326
2327
2328 7.1. Timing
2329 -----------
2330 The node graph supports starting and stopping nodes at scheduled times. This is especially useful
2331 for data source nodes where you want to get the node set up, but only start playback at a specific
2332 time. There are two clocks: local and global.
2333
2334 A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can
2335 only be done based on the global clock because the local clock will not be running while the node
2336 is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the
2337 other hand, the local clock only advances when the node's processing callback is fired, and is
2338 advanced based on the output frame count.
2339
2340 To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with
2341 `ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline.
2342 Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time,
2343 and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the
2344 audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these
2345 outside of the node processing callbacks which are always run on the audio thread.
2346
2347 There is basic support for scheduling the starting and stopping of nodes. You can only schedule one
2348 start and one stop at a time. This is mainly intended for putting nodes into a started or stopped
2349 state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited
2350 to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks
2351 of several milliseconds. The following APIs can be used for scheduling node states:
2352
2353 ```c
2354 ma_node_set_state_time()
2355 ma_node_get_state_time()
2356 ```
2357
2358 The time is absolute and must be based on the global clock. An example is below:
2359
2360 ```c
2361 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second.
2362 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds.
2363 ```
2364
2365 An example for changing the state using a relative time.
2366
2367 ```c
2368 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph));
2369 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph));
2370 ```
2371
2372 Note that due to the nature of multi-threading the times may not be 100% exact. If this is an
2373 issue, consider scheduling state changes from within a processing callback. An idea might be to
2374 have some kind of passthrough trigger node that is used specifically for tracking time and handling
2375 events.
2376
2377
2378
2379 7.2. Thread Safety and Locking
2380 ------------------------------
2381 When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's
2382 expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so
2383 without the use of any locks. This section discusses the implementation used by miniaudio and goes
2384 over some of the compromises employed by miniaudio to achieve this goal. Note that the current
2385 implementation may not be ideal - feedback and critiques are most welcome.
2386
2387 The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected
2388 to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the
2389 implementation, but are crafted in a way such that such locking is not required when reading audio
2390 data from the graph. Locking in these areas are achieved by means of spinlocks.
2391
2392 The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact
2393 that a node can be uninitialized, and it's memory potentially freed, while in the middle of being
2394 processed on the audio thread. There are times when the audio thread will be referencing a node,
2395 which means the uninitialization process of a node needs to make sure it delays returning until the
2396 audio thread is finished so that control is not handed back to the caller thereby giving them a
2397 chance to free the node's memory.
2398
2399 When the audio thread is processing a node, it does so by reading from each of the output buses of
2400 the node. In order for a node to process data for one of it's output buses, it needs to read from
2401 each of it's input buses, and so on an so forth. It follows that once all output buses of a node
2402 are detached, the node as a whole will be disconnected and no further processing will occur unless
2403 it's output buses are reattached, which won't be happening when the node is being uninitialized.
2404 By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can
2405 simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By
2406 doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output
2407 nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean
2408 up.
2409
2410 With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as
2411 it takes to process the output bus being detached. This will happen if it's called at just the
2412 wrong moment where the audio thread has just iterated it and has just started processing. The
2413 caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which
2414 includes the cost of recursively processing it's inputs. This is the biggest compromise made with
2415 the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes
2416 earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching
2417 higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
2418 detachments, detach starting from the lowest level nodes and work your way towards the final
2419 endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
2420 running, detachment will be fast and detachment in any order will be the same. The reason nodes
2421 need to wait for their input attachments to complete is due to the potential for desyncs between
2422 data sources. If the node was to terminate processing mid way through processing it's inputs,
2423 there's a chance that some of the underlying data sources will have been read, but then others not.
2424 That will then result in a potential desynchronization when detaching and reattaching higher-level
2425 nodes. A possible solution to this is to have an option when detaching to terminate processing
2426 before processing all input attachments which should be fairly simple.
2427
2428 Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
2429 locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
2430 for each input bus and output bus. When an output bus is connected to an input bus, both the output
2431 bus and input bus is locked. This locking is specifically for attaching and detaching across
2432 different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
2433 unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
2434 considering that iterating over attachments must not break as a result of attaching or detaching a
2435 node while iteration is occurring.
2436
2437 Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
2438 bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
2439 each item in the list is and output bus. We have some intentional (and convenient) restrictions on
2440 what can done with the linked list in order to simplify the implementation. First of all, whenever
2441 something needs to iterate over the list, it must do so in a forward direction. Backwards iteration
2442 is not supported. Also, items can only be added to the start of the list.
2443
2444 The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer
2445 to the next item in the list, and another to the previous item. A pointer to the previous item is
2446 only required for fast detachment of the node - it is never used in iteration. This is an
2447 important property because it means from the perspective of iteration, attaching and detaching of
2448 an item can be done with a single atomic assignment. This is exploited by both the attachment and
2449 detachment process. When attaching the node, the first thing that is done is the setting of the
2450 local "next" and "previous" pointers of the node. After that, the item is "attached" to the list
2451 by simply performing an atomic exchange with the head pointer. After that, the node is "attached"
2452 to the list from the perspective of iteration. Even though the "previous" pointer of the next item
2453 hasn't yet been set, from the perspective of iteration it's been attached because iteration will
2454 only be happening in a forward direction which means the "previous" pointer won't actually ever get
2455 used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and
2456 `ma_node_detach_output_bus()` for the implementation of this mechanism.
2457
2458
2459
2460 8. Decoding
2461 ===========
2462 The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
2463 devices and can be used independently. The following formats are supported:
2464
2465 +---------+------------------+----------+
2466 | Format | Decoding Backend | Built-In |
2467 +---------+------------------+----------+
2468 | WAV | dr_wav | Yes |
2469 | MP3 | dr_mp3 | Yes |
2470 | FLAC | dr_flac | Yes |
2471 | Vorbis | stb_vorbis | No |
2472 +---------+------------------+----------+
2473
2474 Vorbis is supported via stb_vorbis which can be enabled by including the header section before the
2475 implementation of miniaudio, like the following:
2476
2477 ```c
2478 #define STB_VORBIS_HEADER_ONLY
2479 #include "extras/stb_vorbis.c" // Enables Vorbis decoding.
2480
2481 #define MINIAUDIO_IMPLEMENTATION
2482 #include "miniaudio.h"
2483
2484 // The stb_vorbis implementation must come after the implementation of miniaudio.
2485 #undef STB_VORBIS_HEADER_ONLY
2486 #include "extras/stb_vorbis.c"
2487 ```
2488
2489 A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
2490
2491 Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the
2492 built-in decoders by specifying one or more of the following options before the miniaudio
2493 implementation:
2494
2495 ```c
2496 #define MA_NO_WAV
2497 #define MA_NO_MP3
2498 #define MA_NO_FLAC
2499 ```
2500
2501 Disabling built-in decoding libraries is useful if you use these libraries independently of the
2502 `ma_decoder` API.
2503
2504 A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with
2505 `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is
2506 an example for loading a decoder from a file:
2507
2508 ```c
2509 ma_decoder decoder;
2510 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder);
2511 if (result != MA_SUCCESS) {
2512 return false; // An error occurred.
2513 }
2514
2515 ...
2516
2517 ma_decoder_uninit(&decoder);
2518 ```
2519
2520 When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object
2521 (the `NULL` argument in the example above) which allows you to configure the output format, channel
2522 count, sample rate and channel map:
2523
2524 ```c
2525 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);
2526 ```
2527
2528 When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the
2529 same as that defined by the decoding backend.
2530
2531 Data is read from the decoder as PCM frames. This will output the number of PCM frames actually
2532 read. If this is less than the requested number of PCM frames it means you've reached the end. The
2533 return value will be `MA_AT_END` if no samples have been read and the end has been reached.
2534
2535 ```c
2536 ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead);
2537 if (framesRead < framesToRead) {
2538 // Reached the end.
2539 }
2540 ```
2541
2542 You can also seek to a specific frame like so:
2543
2544 ```c
2545 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
2546 if (result != MA_SUCCESS) {
2547 return false; // An error occurred.
2548 }
2549 ```
2550
2551 If you want to loop back to the start, you can simply seek back to the first PCM frame:
2552
2553 ```c
2554 ma_decoder_seek_to_pcm_frame(pDecoder, 0);
2555 ```
2556
2557 When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding
2558 backend. This can be unnecessarily inefficient if the type is already known. In this case you can
2559 use `encodingFormat` variable in the device config to specify a specific encoding format you want
2560 to decode:
2561
2562 ```c
2563 decoderConfig.encodingFormat = ma_encoding_format_wav;
2564 ```
2565
2566 See the `ma_encoding_format` enum for possible encoding formats.
2567
2568 The `ma_decoder_init_file()` API will try using the file extension to determine which decoding
2569 backend to prefer.
2570
2571
2572 8.1. Custom Decoders
2573 --------------------
2574 It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful
2575 when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of
2576 the stock formats supported by miniaudio. This can be put to particularly good use when using the
2577 `ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for
2578 example, you wanted to support Opus, you can do so with a custom decoder (there if a reference
2579 Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile).
2580
2581 A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs
2582 to be implemented which is then passed into the decoder config:
2583
2584 ```c
2585 ma_decoding_backend_vtable* pCustomBackendVTables[] =
2586 {
2587 &g_ma_decoding_backend_vtable_libvorbis,
2588 &g_ma_decoding_backend_vtable_libopus
2589 };
2590
2591 ...
2592
2593 decoderConfig = ma_decoder_config_init_default();
2594 decoderConfig.pCustomBackendUserData = NULL;
2595 decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;
2596 decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
2597 ```
2598
2599 The `ma_decoding_backend_vtable` vtable has the following functions:
2600
2601 ```
2602 onInit
2603 onInitFile
2604 onInitFileW
2605 onInitMemory
2606 onUninit
2607 ```
2608
2609 There are only two functions that must be implemented - `onInit` and `onUninit`. The other
2610 functions can be implemented for a small optimization for loading from a file path or memory. If
2611 these are not specified, miniaudio will deal with it for you via a generic implementation.
2612
2613 When you initialize a custom data source (by implementing the `onInit` function in the vtable) you
2614 will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the
2615 section about data sources for details on how to implement this. Alternatively, see the
2616 "custom_decoders" example in the miniaudio repository.
2617
2618 The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data
2619 from some arbitrary source. You'll use these functions to read from the raw data and perform the
2620 decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant
2621 parameter.
2622
2623 The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only
2624 used as a hint and can be ignored. However, if any of the properties are relevant to your decoder,
2625 an optimal implementation will handle the relevant properties appropriately.
2626
2627 If memory allocation is required, it should be done so via the specified allocation callbacks if
2628 possible (the `pAllocationCallbacks` parameter).
2629
2630 If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to
2631 NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned.
2632 When multiple custom backends are specified, miniaudio will cycle through the vtables in the order
2633 they're listed in the array that's passed into the decoder config so it's important that your
2634 initialization routine is clean.
2635
2636 When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an
2637 opportunity to clean up and internal data.
2638
2639
2640
2641 9. Encoding
2642 ===========
2643 The `ma_encoding` API is used for writing audio files. The only supported output format is WAV
2644 which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio.
2645 This can be disabled by specifying the following option before the implementation of miniaudio:
2646
2647 ```c
2648 #define MA_NO_WAV
2649 ```
2650
2651 An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data
2652 delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder
2653 to output to a file.
2654
2655 ```c
2656 ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);
2657 ma_encoder encoder;
2658 ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder);
2659 if (result != MA_SUCCESS) {
2660 // Error
2661 }
2662
2663 ...
2664
2665 ma_encoder_uninit(&encoder);
2666 ```
2667
2668 When initializing an encoder you must specify a config which is initialized with
2669 `ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output
2670 channel count and output sample rate. The following file types are supported:
2671
2672 +------------------------+-------------+
2673 | Enum | Description |
2674 +------------------------+-------------+
2675 | ma_encoding_format_wav | WAV |
2676 +------------------------+-------------+
2677
2678 If the format, channel count or sample rate is not supported by the output file type an error will
2679 be returned. The encoder will not perform data conversion so you will need to convert it before
2680 outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the
2681 example below:
2682
2683 ```c
2684 framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
2685 ```
2686
2687 Encoders must be uninitialized with `ma_encoder_uninit()`.
2688
2689
2690
2691 10. Data Conversion
2692 ===================
2693 A data conversion API is included with miniaudio which supports the majority of data conversion
2694 requirements. This supports conversion between sample formats, channel counts (with channel
2695 mapping) and sample rates.
2696
2697
2698 10.1. Sample Format Conversion
2699 ------------------------------
2700 Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and
2701 `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific
2702 formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use
2703 `ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count
2704 and channel count as a variable instead of the total sample count.
2705
2706
2707 10.1.1. Dithering
2708 -----------------
2709 Dithering can be set using the ditherMode parameter.
2710
2711 The different dithering modes include the following, in order of efficiency:
2712
2713 +-----------+--------------------------+
2714 | Type | Enum Token |
2715 +-----------+--------------------------+
2716 | None | ma_dither_mode_none |
2717 | Rectangle | ma_dither_mode_rectangle |
2718 | Triangle | ma_dither_mode_triangle |
2719 +-----------+--------------------------+
2720
2721 Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be
2722 ignored for conversions where dithering is not needed. Dithering is available for the following
2723 conversions:
2724
2725 ```
2726 s16 -> u8
2727 s24 -> u8
2728 s32 -> u8
2729 f32 -> u8
2730 s24 -> s16
2731 s32 -> s16
2732 f32 -> s16
2733 ```
2734
2735 Note that it is not an error to pass something other than ma_dither_mode_none for conversions where
2736 dither is not used. It will just be ignored.
2737
2738
2739
2740 10.2. Channel Conversion
2741 ------------------------
2742 Channel conversion is used for channel rearrangement and conversion from one channel count to
2743 another. The `ma_channel_converter` API is used for channel conversion. Below is an example of
2744 initializing a simple channel converter which converts from mono to stereo.
2745
2746 ```c
2747 ma_channel_converter_config config = ma_channel_converter_config_init(
2748 ma_format, // Sample format
2749 1, // Input channels
2750 NULL, // Input channel map
2751 2, // Output channels
2752 NULL, // Output channel map
2753 ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels.
2754
2755 result = ma_channel_converter_init(&config, NULL, &converter);
2756 if (result != MA_SUCCESS) {
2757 // Error.
2758 }
2759 ```
2760
2761 To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
2762
2763 ```c
2764 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
2765 if (result != MA_SUCCESS) {
2766 // Error.
2767 }
2768 ```
2769
2770 It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM
2771 frames.
2772
2773 Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
2774
2775
2776 10.2.1. Channel Mapping
2777 -----------------------
2778 In addition to converting from one channel count to another, like the example above, the channel
2779 converter can also be used to rearrange channels. When initializing the channel converter, you can
2780 optionally pass in channel maps for both the input and output frames. If the channel counts are the
2781 same, and each channel map contains the same channel positions with the exception that they're in
2782 a different order, a simple shuffling of the channels will be performed. If, however, there is not
2783 a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed
2784 based on a mixing mode which is specified when initializing the `ma_channel_converter_config`
2785 object.
2786
2787 When converting from mono to multi-channel, the mono channel is simply copied to each output
2788 channel. When going the other way around, the audio of each output channel is simply averaged and
2789 copied to the mono channel.
2790
2791 In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess
2792 channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th
2793 channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and
2794 4th channels.
2795
2796 The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a
2797 simple distribution between input and output. Imagine sitting in the middle of a room, with
2798 speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be
2799 thought of as being in the corner of the front and left walls.
2800
2801 Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined
2802 weights. Custom weights can be passed in as the last parameter of
2803 `ma_channel_converter_config_init()`.
2804
2805 Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a
2806 `ma_standard_channel_map` enum as it's first parameter, which can be one of the following:
2807
2808 +-----------------------------------+-----------------------------------------------------------+
2809 | Name | Description |
2810 +-----------------------------------+-----------------------------------------------------------+
2811 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. |
2812 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. |
2813 | ma_standard_channel_map_alsa | Default ALSA channel map. |
2814 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. |
2815 | ma_standard_channel_map_flac | FLAC channel map. |
2816 | ma_standard_channel_map_vorbis | Vorbis channel map. |
2817 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). |
2818 | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. |
2819 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering |
2820 +-----------------------------------+-----------------------------------------------------------+
2821
2822 Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`):
2823
2824 +---------------+---------------------------------+
2825 | Channel Count | Mapping |
2826 +---------------+---------------------------------+
2827 | 1 (Mono) | 0: MA_CHANNEL_MONO |
2828 +---------------+---------------------------------+
2829 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2830 | | 1: MA_CHANNEL_FRONT_RIGHT |
2831 +---------------+---------------------------------+
2832 | 3 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2833 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2834 | | 2: MA_CHANNEL_FRONT_CENTER |
2835 +---------------+---------------------------------+
2836 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2837 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2838 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2839 | | 3: MA_CHANNEL_BACK_CENTER |
2840 +---------------+---------------------------------+
2841 | 5 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2842 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2843 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2844 | | 3: MA_CHANNEL_BACK_LEFT <br> |
2845 | | 4: MA_CHANNEL_BACK_RIGHT |
2846 +---------------+---------------------------------+
2847 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2848 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2849 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2850 | | 3: MA_CHANNEL_LFE <br> |
2851 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
2852 | | 5: MA_CHANNEL_SIDE_RIGHT |
2853 +---------------+---------------------------------+
2854 | 7 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2855 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2856 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2857 | | 3: MA_CHANNEL_LFE <br> |
2858 | | 4: MA_CHANNEL_BACK_CENTER <br> |
2859 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
2860 | | 5: MA_CHANNEL_SIDE_RIGHT |
2861 +---------------+---------------------------------+
2862 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2863 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2864 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2865 | | 3: MA_CHANNEL_LFE <br> |
2866 | | 4: MA_CHANNEL_BACK_LEFT <br> |
2867 | | 5: MA_CHANNEL_BACK_RIGHT <br> |
2868 | | 6: MA_CHANNEL_SIDE_LEFT <br> |
2869 | | 7: MA_CHANNEL_SIDE_RIGHT |
2870 +---------------+---------------------------------+
2871 | Other | All channels set to 0. This |
2872 | | is equivalent to the same |
2873 | | mapping as the device. |
2874 +---------------+---------------------------------+
2875
2876
2877
2878 10.3. Resampling
2879 ----------------
2880 Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something
2881 like the following:
2882
2883 ```c
2884 ma_resampler_config config = ma_resampler_config_init(
2885 ma_format_s16,
2886 channels,
2887 sampleRateIn,
2888 sampleRateOut,
2889 ma_resample_algorithm_linear);
2890
2891 ma_resampler resampler;
2892 ma_result result = ma_resampler_init(&config, &resampler);
2893 if (result != MA_SUCCESS) {
2894 // An error occurred...
2895 }
2896 ```
2897
2898 Do the following to uninitialize the resampler:
2899
2900 ```c
2901 ma_resampler_uninit(&resampler);
2902 ```
2903
2904 The following example shows how data can be processed
2905
2906 ```c
2907 ma_uint64 frameCountIn = 1000;
2908 ma_uint64 frameCountOut = 2000;
2909 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
2910 if (result != MA_SUCCESS) {
2911 // An error occurred...
2912 }
2913
2914 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the
2915 // number of output frames written.
2916 ```
2917
2918 To initialize the resampler you first need to set up a config (`ma_resampler_config`) with
2919 `ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of
2920 channels, the input and output sample rate, and the algorithm.
2921
2922 The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format
2923 you will need to perform pre- and post-conversions yourself where necessary. Note that the format
2924 is the same for both input and output. The format cannot be changed after initialization.
2925
2926 The resampler supports multiple channels and is always interleaved (both input and output). The
2927 channel count cannot be changed after initialization.
2928
2929 The sample rates can be anything other than zero, and are always specified in hertz. They should be
2930 set to something like 44100, etc. The sample rate is the only configuration property that can be
2931 changed after initialization.
2932
2933 The miniaudio resampler has built-in support for the following algorithms:
2934
2935 +-----------+------------------------------+
2936 | Algorithm | Enum Token |
2937 +-----------+------------------------------+
2938 | Linear | ma_resample_algorithm_linear |
2939 | Custom | ma_resample_algorithm_custom |
2940 +-----------+------------------------------+
2941
2942 The algorithm cannot be changed after initialization.
2943
2944 Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
2945 De-interleaved processing is not supported. To process frames, use
2946 `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you
2947 can fit in the output buffer and the number of input frames contained in the input buffer. On
2948 output these variables contain the number of output frames that were written to the output buffer
2949 and the number of input frames that were consumed in the process. You can pass in NULL for the
2950 input buffer in which case it will be treated as an infinitely large buffer of zeros. The output
2951 buffer can also be NULL, in which case the processing will be treated as seek.
2952
2953 The sample rate can be changed dynamically on the fly. You can change this with explicit sample
2954 rates with `ma_resampler_set_rate()` and also with a decimal ratio with
2955 `ma_resampler_set_rate_ratio()`. The ratio is in/out.
2956
2957 Sometimes it's useful to know exactly how many input frames will be required to output a specific
2958 number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`.
2959 Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
2960 number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
2961
2962 Due to the nature of how resampling works, the resampler introduces some latency. This can be
2963 retrieved in terms of both the input rate and the output rate with
2964 `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
2965
2966
2967 10.3.1. Resampling Algorithms
2968 -----------------------------
2969 The choice of resampling algorithm depends on your situation and requirements.
2970
2971
2972 10.3.1.1. Linear Resampling
2973 ---------------------------
2974 The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however,
2975 some control over the quality of the linear resampler which may make it a suitable option depending
2976 on your requirements.
2977
2978 The linear resampler performs low-pass filtering before or after downsampling or upsampling,
2979 depending on the sample rates you're converting between. When decreasing the sample rate, the
2980 low-pass filter will be applied before downsampling. When increasing the rate it will be performed
2981 after upsampling. By default a fourth order low-pass filter will be applied. This can be configured
2982 via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering.
2983
2984 The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of
2985 the input and output sample rates (Nyquist Frequency).
2986
2987 The API for the linear resampler is the same as the main resampler API, only it's called
2988 `ma_linear_resampler`.
2989
2990
2991 10.3.2. Custom Resamplers
2992 -------------------------
2993 You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling
2994 algorithm and setting a vtable in the resampler config:
2995
2996 ```c
2997 ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom);
2998 config.pBackendVTable = &g_customResamplerVTable;
2999 ```
3000
3001 Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You
3002 need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all
3003 functions in the vtable need to be implemented, but if it's possible to implement, they should be.
3004
3005 You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The
3006 `onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom
3007 resampler will need to make given the supplied config. When you initialize the resampler via the
3008 `onInit` callback, you'll be given a pointer to a heap allocation which is where you should store
3009 the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage
3010 it for you.
3011
3012 The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn`
3013 points to a variable containing the number of frames in the `pFramesIn` buffer and
3014 `pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer.
3015 On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed,
3016 whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`.
3017
3018 The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If
3019 dynamic rate changes are not supported, you can set this callback to NULL.
3020
3021 The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in
3022 input and output rates respectively. These can be NULL in which case latency calculations will be
3023 assumed to be NULL.
3024
3025 The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input
3026 frames are required to be available to produce the given number of output frames. Likewise, the
3027 `onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be
3028 produced given the specified number of input frames. miniaudio will use these as a hint, but they
3029 are optional and can be set to NULL if you're unable to implement them.
3030
3031
3032
3033 10.4. General Data Conversion
3034 -----------------------------
3035 The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and
3036 resampling into one operation. This is what miniaudio uses internally to convert between the format
3037 requested when the device was initialized and the format of the backend's native device. The API
3038 for general data conversion is very similar to the resampling API. Create a `ma_data_converter`
3039 object like this:
3040
3041 ```c
3042 ma_data_converter_config config = ma_data_converter_config_init(
3043 inputFormat,
3044 outputFormat,
3045 inputChannels,
3046 outputChannels,
3047 inputSampleRate,
3048 outputSampleRate
3049 );
3050
3051 ma_data_converter converter;
3052 ma_result result = ma_data_converter_init(&config, NULL, &converter);
3053 if (result != MA_SUCCESS) {
3054 // An error occurred...
3055 }
3056 ```
3057
3058 In the example above we use `ma_data_converter_config_init()` to initialize the config, however
3059 there's many more properties that can be configured, such as channel maps and resampling quality.
3060 Something like the following may be more suitable depending on your requirements:
3061
3062 ```c
3063 ma_data_converter_config config = ma_data_converter_config_init_default();
3064 config.formatIn = inputFormat;
3065 config.formatOut = outputFormat;
3066 config.channelsIn = inputChannels;
3067 config.channelsOut = outputChannels;
3068 config.sampleRateIn = inputSampleRate;
3069 config.sampleRateOut = outputSampleRate;
3070 ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn);
3071 config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;
3072 ```
3073
3074 Do the following to uninitialize the data converter:
3075
3076 ```c
3077 ma_data_converter_uninit(&converter, NULL);
3078 ```
3079
3080 The following example shows how data can be processed
3081
3082 ```c
3083 ma_uint64 frameCountIn = 1000;
3084 ma_uint64 frameCountOut = 2000;
3085 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
3086 if (result != MA_SUCCESS) {
3087 // An error occurred...
3088 }
3089
3090 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number
3091 // of output frames written.
3092 ```
3093
3094 The data converter supports multiple channels and is always interleaved (both input and output).
3095 The channel count cannot be changed after initialization.
3096
3097 Sample rates can be anything other than zero, and are always specified in hertz. They should be set
3098 to something like 44100, etc. The sample rate is the only configuration property that can be
3099 changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of
3100 `ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use
3101 `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out.
3102 The resampling algorithm cannot be changed after initialization.
3103
3104 Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
3105 De-interleaved processing is not supported. To process frames, use
3106 `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames
3107 you can fit in the output buffer and the number of input frames contained in the input buffer. On
3108 output these variables contain the number of output frames that were written to the output buffer
3109 and the number of input frames that were consumed in the process. You can pass in NULL for the
3110 input buffer in which case it will be treated as an infinitely large
3111 buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated
3112 as seek.
3113
3114 Sometimes it's useful to know exactly how many input frames will be required to output a specific
3115 number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`.
3116 Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
3117 number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
3118
3119 Due to the nature of how resampling works, the data converter introduces some latency if resampling
3120 is required. This can be retrieved in terms of both the input rate and the output rate with
3121 `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
3122
3123
3124
3125 11. Filtering
3126 =============
3127
3128 11.1. Biquad Filtering
3129 ----------------------
3130 Biquad filtering is achieved with the `ma_biquad` API. Example:
3131
3132 ```c
3133 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
3134 ma_result result = ma_biquad_init(&config, &biquad);
3135 if (result != MA_SUCCESS) {
3136 // Error.
3137 }
3138
3139 ...
3140
3141 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
3142 ```
3143
3144 Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0,
3145 b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and
3146 coefficients must not be pre-normalized.
3147
3148 Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format
3149 you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use
3150 fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.
3151
3152 Input and output frames are always interleaved.
3153
3154 Filtering can be applied in-place by passing in the same pointer for both the input and output
3155 buffers, like so:
3156
3157 ```c
3158 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
3159 ```
3160
3161 If you need to change the values of the coefficients, but maintain the values in the registers you
3162 can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the
3163 filter while keeping the values of registers valid to avoid glitching. Do not use
3164 `ma_biquad_init()` for this as it will do a full initialization which involves clearing the
3165 registers to 0. Note that changing the format or channel count after initialization is invalid and
3166 will result in an error.
3167
3168
3169 11.2. Low-Pass Filtering
3170 ------------------------
3171 Low-pass filtering is achieved with the following APIs:
3172
3173 +---------+------------------------------------------+
3174 | API | Description |
3175 +---------+------------------------------------------+
3176 | ma_lpf1 | First order low-pass filter |
3177 | ma_lpf2 | Second order low-pass filter |
3178 | ma_lpf | High order low-pass filter (Butterworth) |
3179 +---------+------------------------------------------+
3180
3181 Low-pass filter example:
3182
3183 ```c
3184 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
3185 ma_result result = ma_lpf_init(&config, &lpf);
3186 if (result != MA_SUCCESS) {
3187 // Error.
3188 }
3189
3190 ...
3191
3192 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
3193 ```
3194
3195 Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format
3196 you need to convert it yourself beforehand. Input and output frames are always interleaved.
3197
3198 Filtering can be applied in-place by passing in the same pointer for both the input and output
3199 buffers, like so:
3200
3201 ```c
3202 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
3203 ```
3204
3205 The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more,
3206 you can chain first and second order filters together.
3207
3208 ```c
3209 for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
3210 ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
3211 }
3212 ```
3213
3214 If you need to change the configuration of the filter, but need to maintain the state of internal
3215 registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample
3216 rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the
3217 format or channel count after initialization is invalid and will result in an error.
3218
3219 The `ma_lpf` object supports a configurable order, but if you only need a first order filter you
3220 may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use
3221 `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient.
3222
3223 If an even filter order is specified, a series of second order filters will be processed in a
3224 chain. If an odd filter order is specified, a first order filter will be applied, followed by a
3225 series of second order filters in a chain.
3226
3227
3228 11.3. High-Pass Filtering
3229 -------------------------
3230 High-pass filtering is achieved with the following APIs:
3231
3232 +---------+-------------------------------------------+
3233 | API | Description |
3234 +---------+-------------------------------------------+
3235 | ma_hpf1 | First order high-pass filter |
3236 | ma_hpf2 | Second order high-pass filter |
3237 | ma_hpf | High order high-pass filter (Butterworth) |
3238 +---------+-------------------------------------------+
3239
3240 High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`,
3241 `ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage.
3242
3243
3244 11.4. Band-Pass Filtering
3245 -------------------------
3246 Band-pass filtering is achieved with the following APIs:
3247
3248 +---------+-------------------------------+
3249 | API | Description |
3250 +---------+-------------------------------+
3251 | ma_bpf2 | Second order band-pass filter |
3252 | ma_bpf | High order band-pass filter |
3253 +---------+-------------------------------+
3254
3255 Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and
3256 `ma_hpf`. See example code for low-pass filters for example usage. Note that the order for
3257 band-pass filters must be an even number which means there is no first order band-pass filter,
3258 unlike low-pass and high-pass filters.
3259
3260
3261 11.5. Notch Filtering
3262 ---------------------
3263 Notch filtering is achieved with the following APIs:
3264
3265 +-----------+------------------------------------------+
3266 | API | Description |
3267 +-----------+------------------------------------------+
3268 | ma_notch2 | Second order notching filter |
3269 +-----------+------------------------------------------+
3270
3271
3272 11.6. Peaking EQ Filtering
3273 -------------------------
3274 Peaking filtering is achieved with the following APIs:
3275
3276 +----------+------------------------------------------+
3277 | API | Description |
3278 +----------+------------------------------------------+
3279 | ma_peak2 | Second order peaking filter |
3280 +----------+------------------------------------------+
3281
3282
3283 11.7. Low Shelf Filtering
3284 -------------------------
3285 Low shelf filtering is achieved with the following APIs:
3286
3287 +-------------+------------------------------------------+
3288 | API | Description |
3289 +-------------+------------------------------------------+
3290 | ma_loshelf2 | Second order low shelf filter |
3291 +-------------+------------------------------------------+
3292
3293 Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to
3294 just turn them down rather than eliminate them entirely.
3295
3296
3297 11.8. High Shelf Filtering
3298 --------------------------
3299 High shelf filtering is achieved with the following APIs:
3300
3301 +-------------+------------------------------------------+
3302 | API | Description |
3303 +-------------+------------------------------------------+
3304 | ma_hishelf2 | Second order high shelf filter |
3305 +-------------+------------------------------------------+
3306
3307 The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf`
3308 instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies,
3309 the high shelf filter does the same thing for high frequencies.
3310
3311
3312
3313
3314 12. Waveform and Noise Generation
3315 =================================
3316
3317 12.1. Waveforms
3318 ---------------
3319 miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved
3320 with the `ma_waveform` API. Example:
3321
3322 ```c
3323 ma_waveform_config config = ma_waveform_config_init(
3324 FORMAT,
3325 CHANNELS,
3326 SAMPLE_RATE,
3327 ma_waveform_type_sine,
3328 amplitude,
3329 frequency);
3330
3331 ma_waveform waveform;
3332 ma_result result = ma_waveform_init(&config, &waveform);
3333 if (result != MA_SUCCESS) {
3334 // Error.
3335 }
3336
3337 ...
3338
3339 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);
3340 ```
3341
3342 The amplitude, frequency, type, and sample rate can be changed dynamically with
3343 `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and
3344 `ma_waveform_set_sample_rate()` respectively.
3345
3346 You can invert the waveform by setting the amplitude to a negative value. You can use this to
3347 control whether or not a sawtooth has a positive or negative ramp, for example.
3348
3349 Below are the supported waveform types:
3350
3351 +---------------------------+
3352 | Enum Name |
3353 +---------------------------+
3354 | ma_waveform_type_sine |
3355 | ma_waveform_type_square |
3356 | ma_waveform_type_triangle |
3357 | ma_waveform_type_sawtooth |
3358 +---------------------------+
3359
3360
3361
3362 12.2. Noise
3363 -----------
3364 miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example:
3365
3366 ```c
3367 ma_noise_config config = ma_noise_config_init(
3368 FORMAT,
3369 CHANNELS,
3370 ma_noise_type_white,
3371 SEED,
3372 amplitude);
3373
3374 ma_noise noise;
3375 ma_result result = ma_noise_init(&config, &noise);
3376 if (result != MA_SUCCESS) {
3377 // Error.
3378 }
3379
3380 ...
3381
3382 ma_noise_read_pcm_frames(&noise, pOutput, frameCount);
3383 ```
3384
3385 The noise API uses simple LCG random number generation. It supports a custom seed which is useful
3386 for things like automated testing requiring reproducibility. Setting the seed to zero will default
3387 to `MA_DEFAULT_LCG_SEED`.
3388
3389 The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and
3390 `ma_noise_set_seed()` respectively.
3391
3392 By default, the noise API will use different values for different channels. So, for example, the
3393 left side in a stereo stream will be different to the right side. To instead have each channel use
3394 the same random value, set the `duplicateChannels` member of the noise config to true, like so:
3395
3396 ```c
3397 config.duplicateChannels = MA_TRUE;
3398 ```
3399
3400 Below are the supported noise types.
3401
3402 +------------------------+
3403 | Enum Name |
3404 +------------------------+
3405 | ma_noise_type_white |
3406 | ma_noise_type_pink |
3407 | ma_noise_type_brownian |
3408 +------------------------+
3409
3410
3411
3412 13. Audio Buffers
3413 =================
3414 miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can
3415 read from memory that's managed by the application, but can also handle the memory management for
3416 you internally. Memory management is flexible and should support most use cases.
3417
3418 Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
3419
3420 ```c
3421 ma_audio_buffer_config config = ma_audio_buffer_config_init(
3422 format,
3423 channels,
3424 sizeInFrames,
3425 pExistingData,
3426 &allocationCallbacks);
3427
3428 ma_audio_buffer buffer;
3429 result = ma_audio_buffer_init(&config, &buffer);
3430 if (result != MA_SUCCESS) {
3431 // Error.
3432 }
3433
3434 ...
3435
3436 ma_audio_buffer_uninit(&buffer);
3437 ```
3438
3439 In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an
3440 application can do self-managed memory allocation. If you would rather make a copy of the data, use
3441 `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.
3442
3443 Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the
3444 raw audio data in a contiguous block of memory. That is, the raw audio data will be located
3445 immediately after the `ma_audio_buffer` structure. To do this, use
3446 `ma_audio_buffer_alloc_and_init()`:
3447
3448 ```c
3449 ma_audio_buffer_config config = ma_audio_buffer_config_init(
3450 format,
3451 channels,
3452 sizeInFrames,
3453 pExistingData,
3454 &allocationCallbacks);
3455
3456 ma_audio_buffer* pBuffer
3457 result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);
3458 if (result != MA_SUCCESS) {
3459 // Error
3460 }
3461
3462 ...
3463
3464 ma_audio_buffer_uninit_and_free(&buffer);
3465 ```
3466
3467 If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it
3468 with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by
3469 `pExistingData` will be copied into the buffer, which is contrary to the behavior of
3470 `ma_audio_buffer_init()`.
3471
3472 An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the
3473 cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should
3474 loop. The return value is the number of frames actually read. If this is less than the number of
3475 frames requested it means the end has been reached. This should never happen if the `loop`
3476 parameter is set to true. If you want to manually loop back to the start, you can do so with with
3477 `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an
3478 audio buffer.
3479
3480 ```c
3481 ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);
3482 if (framesRead < desiredFrameCount) {
3483 // If not looping, this means the end has been reached. This should never happen in looping mode with valid input.
3484 }
3485 ```
3486
3487 Sometimes you may want to avoid the cost of data movement between the internal buffer and the
3488 output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data:
3489
3490 ```c
3491 void* pMappedFrames;
3492 ma_uint64 frameCount = frameCountToTryMapping;
3493 ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);
3494 if (result == MA_SUCCESS) {
3495 // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be
3496 // less due to the end of the buffer being reached.
3497 ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
3498
3499 // You must unmap the buffer.
3500 ma_audio_buffer_unmap(pAudioBuffer, frameCount);
3501 }
3502 ```
3503
3504 When you use memory mapping, the read cursor is increment by the frame count passed in to
3505 `ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller
3506 than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is
3507 that it does not handle looping for you. You can determine if the buffer is at the end for the
3508 purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of
3509 `ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END`
3510 as an error when returned by `ma_audio_buffer_unmap()`.
3511
3512
3513
3514 14. Ring Buffers
3515 ================
3516 miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via
3517 the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb`
3518 operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around
3519 `ma_rb`.
3520
3521 Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved
3522 streams. The caller can also allocate their own backing memory for the ring buffer to use
3523 internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for
3524 you.
3525
3526 The examples below use the PCM frame variant of the ring buffer since that's most likely the one
3527 you will want to use. To initialize a ring buffer, do something like the following:
3528
3529 ```c
3530 ma_pcm_rb rb;
3531 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
3532 if (result != MA_SUCCESS) {
3533 // Error
3534 }
3535 ```
3536
3537 The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
3538 it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you
3539 would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
3540 instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
3541 is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
3542 Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.
3543
3544 Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is
3545 offset from each other based on the stride. To manage your sub-buffers you can use
3546 `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and
3547 `ma_pcm_rb_get_subbuffer_ptr()`.
3548
3549 Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section
3550 of the ring buffer. You specify the number of frames you need, and on output it will set to what
3551 was actually acquired. If the read or write pointer is positioned such that the number of frames
3552 requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number
3553 of frames you're given may be less than the number you requested.
3554
3555 After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the
3556 buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is
3557 where the read/write pointers are updated. When you commit you need to pass in the buffer that was
3558 returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is
3559 only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and
3560 `ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was
3561 originally requested.
3562
3563 If you want to correct for drift between the write pointer and the read pointer you can use a
3564 combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and
3565 `ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only
3566 move the read pointer forward via the consumer thread, and the write pointer forward by the
3567 producer thread. If there is too much space between the pointers, move the read pointer forward. If
3568 there is too little space between the pointers, move the write pointer forward.
3569
3570 You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb`
3571 API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and
3572 instead of frame counts you will pass around byte counts.
3573
3574 The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most
3575 significant bit being used to encode a loop flag and the internally managed buffers always being
3576 aligned to `MA_SIMD_ALIGNMENT`.
3577
3578 Note that the ring buffer is only thread safe when used by a single consumer thread and single
3579 producer thread.
3580
3581
3582
3583 15. Backends
3584 ============
3585 The following backends are supported by miniaudio. These are listed in order of default priority.
3586 When no backend is specified when initializing a context or device, miniaudio will attempt to use
3587 each of these backends in the order listed in the table below.
3588
3589 Note that backends that are not usable by the build target will not be included in the build. For
3590 example, ALSA, which is specific to Linux, will not be included in the Windows build.
3591
3592 +-------------+-----------------------+--------------------------------------------------------+
3593 | Name | Enum Name | Supported Operating Systems |
3594 +-------------+-----------------------+--------------------------------------------------------+
3595 | WASAPI | ma_backend_wasapi | Windows Vista+ |
3596 | DirectSound | ma_backend_dsound | Windows XP+ |
3597 | WinMM | ma_backend_winmm | Windows 95+ |
3598 | Core Audio | ma_backend_coreaudio | macOS, iOS |
3599 | sndio | ma_backend_sndio | OpenBSD |
3600 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
3601 | OSS | ma_backend_oss | FreeBSD |
3602 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
3603 | ALSA | ma_backend_alsa | Linux |
3604 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
3605 | AAudio | ma_backend_aaudio | Android 8+ |
3606 | OpenSL ES | ma_backend_opensl | Android (API level 16+) |
3607 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
3608 | Custom | ma_backend_custom | Cross Platform |
3609 | Null | ma_backend_null | Cross Platform (not used on Web) |
3610 +-------------+-----------------------+--------------------------------------------------------+
3611
3612 Some backends have some nuance details you may want to be aware of.
3613
3614 15.1. WASAPI
3615 ------------
3616 - Low-latency shared mode will be disabled when using an application-defined sample rate which is
3617 different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC`
3618 to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing
3619 when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC
3620 will result in miniaudio's internal resampler being used instead which will in turn enable the
3621 use of low-latency shared mode.
3622
3623 15.2. PulseAudio
3624 ----------------
3625 - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
3626 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling.
3627 Alternatively, consider using a different backend such as ALSA.
3628
3629 15.3. Android
3630 -------------
3631 - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
3632 `<uses-permission android:name="android.permission.RECORD_AUDIO" />`
3633 - With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a
3634 limitation with OpenSL|ES.
3635 - With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration
3636 API (devices are enumerated through Java). You can however perform your own device enumeration
3637 through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it
3638 to ma_device_init().
3639 - The backend API will perform resampling where possible. The reason for this as opposed to using
3640 miniaudio's built-in resampler is to take advantage of any potential device-specific
3641 optimizations the driver may implement.
3642
3643 BSD
3644 ---
3645 - The sndio backend is currently only enabled on OpenBSD builds.
3646 - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
3647 use it.
3648
3649 15.4. UWP
3650 ---------
3651 - UWP only supports default playback and capture devices.
3652 - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
3653
3654 ```
3655 <Package ...>
3656 ...
3657 <Capabilities>
3658 <DeviceCapability Name="microphone" />
3659 </Capabilities>
3660 </Package>
3661 ```
3662
3663 15.5. Web Audio / Emscripten
3664 ----------------------------
3665 - You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build.
3666 - The first time a context is initialized it will create a global object called "miniaudio" whose
3667 primary purpose is to act as a factory for device objects.
3668 - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as
3669 they've been deprecated.
3670 - Google has implemented a policy in their browsers that prevent automatic media output without
3671 first receiving some kind of user input. The following web page has additional details:
3672 https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device
3673 may fail if you try to start playback without first handling some kind of user input.
3674
3675
3676
3677 16. Optimization Tips
3678 =====================
3679 See below for some tips on improving performance.
3680
3681 16.1. Low Level API
3682 -------------------
3683 - In the data callback, if your data is already clipped prior to copying it into the output buffer,
3684 set the `noClip` config option in the device config to true. This will disable miniaudio's built
3685 in clipping function.
3686 - By default, miniaudio will pre-silence the data callback's output buffer. If you know that you
3687 will always write valid data to the output buffer you can disable pre-silencing by setting the
3688 `noPreSilence` config option in the device config to true.
3689
3690 16.2. High Level API
3691 --------------------
3692 - If a sound does not require doppler or pitch shifting, consider disabling pitching by
3693 initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.
3694 - If a sound does not require spatialization, disable it by initializing the sound with the
3695 `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with
3696 `ma_sound_set_spatialization_enabled()`.
3697 - If you know all of your sounds will always be the same sample rate, set the engine's sample
3698 rate to match that of the sounds. Likewise, if you're using a self-managed resource manager,
3699 consider setting the decoded sample rate to match your sounds. By configuring everything to
3700 use a consistent sample rate, sample rate conversion can be avoided.
3701
3702
3703
3704 17. Miscellaneous Notes
3705 =======================
3706 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for
3707 WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though
3708 not all have been tested.
3709 - When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This
3710 is due to 64-bit file APIs not being available.
3711 */
3712
3713 #ifndef miniaudio_h
3714 #define miniaudio_h
3715
3716 #ifdef __cplusplus
3717 extern "C" {
3718 #endif
3719
3720 #define MA_STRINGIFY(x) #x
3721 #define MA_XSTRINGIFY(x) MA_STRINGIFY(x)
3722
3723 #define MA_VERSION_MAJOR 0
3724 #define MA_VERSION_MINOR 11
3725 #define MA_VERSION_REVISION 16
3726 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
3727
3728 #if defined(_MSC_VER) && !defined(__clang__)
3729 #pragma warning(push)
3730 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
3731 #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */
3732 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
3733 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
3734 #pragma GCC diagnostic push
3735 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
3736 #if defined(__clang__)
3737 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
3738 #endif
3739 #endif
3740
3741
3742
3743 #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__)
3744 #define MA_SIZEOF_PTR 8
3745 #else
3746 #define MA_SIZEOF_PTR 4
3747 #endif
3748
3749 #include <stddef.h> /* For size_t. */
3750
3751 /* Sized types. */
3752 #if defined(MA_USE_STDINT)
3753 #include <stdint.h>
3754 typedef int8_t ma_int8;
3755 typedef uint8_t ma_uint8;
3756 typedef int16_t ma_int16;
3757 typedef uint16_t ma_uint16;
3758 typedef int32_t ma_int32;
3759 typedef uint32_t ma_uint32;
3760 typedef int64_t ma_int64;
3761 typedef uint64_t ma_uint64;
3762 #else
3763 typedef signed char ma_int8;
3764 typedef unsigned char ma_uint8;
3765 typedef signed short ma_int16;
3766 typedef unsigned short ma_uint16;
3767 typedef signed int ma_int32;
3768 typedef unsigned int ma_uint32;
3769 #if defined(_MSC_VER) && !defined(__clang__)
3770 typedef signed __int64 ma_int64;
3771 typedef unsigned __int64 ma_uint64;
3772 #else
3773 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
3774 #pragma GCC diagnostic push
3775 #pragma GCC diagnostic ignored "-Wlong-long"
3776 #if defined(__clang__)
3777 #pragma GCC diagnostic ignored "-Wc++11-long-long"
3778 #endif
3779 #endif
3780 typedef signed long long ma_int64;
3781 typedef unsigned long long ma_uint64;
3782 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
3783 #pragma GCC diagnostic pop
3784 #endif
3785 #endif
3786 #endif /* MA_USE_STDINT */
3787
3788 #if MA_SIZEOF_PTR == 8
3789 typedef ma_uint64 ma_uintptr;
3790 #else
3791 typedef ma_uint32 ma_uintptr;
3792 #endif
3793
3794 typedef ma_uint8 ma_bool8;
3795 typedef ma_uint32 ma_bool32;
3796 #define MA_TRUE 1
3797 #define MA_FALSE 0
3798
3799 /* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */
3800 typedef float ma_float;
3801 typedef double ma_double;
3802
3803 typedef void* ma_handle;
3804 typedef void* ma_ptr;
3805
3806 /*
3807 ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting
3808 between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get
3809 warning C4191 about "type cast between incompatible function types". To work around this I'm going
3810 to use a different data type depending on the compiler.
3811 */
3812 #if defined(__GNUC__)
3813 typedef void (*ma_proc)(void);
3814 #else
3815 typedef void* ma_proc;
3816 #endif
3817
3818 #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
3819 typedef ma_uint16 wchar_t;
3820 #endif
3821
3822 /* Define NULL for some compilers. */
3823 #ifndef NULL
3824 #define NULL 0
3825 #endif
3826
3827 #if defined(SIZE_MAX)
3828 #define MA_SIZE_MAX SIZE_MAX
3829 #else
3830 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
3831 #endif
3832
3833
3834 /* Platform/backend detection. */
3835 #if defined(_WIN32) || defined(__COSMOPOLITAN__)
3836 #define MA_WIN32
3837 #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)))
3838 #define MA_WIN32_UWP
3839 #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
3840 #define MA_WIN32_GDK
3841 #else
3842 #define MA_WIN32_DESKTOP
3843 #endif
3844 #endif
3845 #if !defined(_WIN32) /* If it's not Win32, assume POSIX. */
3846 #define MA_POSIX
3847
3848 /*
3849 Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented.
3850 You can use this to avoid including pthread.h in the header section. The downside is that it
3851 results in some fixed sized structures being declared for the various types that are used in
3852 miniaudio. The risk here is that these types might be too small for a given platform. This
3853 risk is yours to take and no support will be offered if you enable this option.
3854 */
3855 #ifndef MA_NO_PTHREAD_IN_HEADER
3856 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
3857 typedef pthread_t ma_pthread_t;
3858 typedef pthread_mutex_t ma_pthread_mutex_t;
3859 typedef pthread_cond_t ma_pthread_cond_t;
3860 #else
3861 typedef ma_uintptr ma_pthread_t;
3862 typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;
3863 typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;
3864 #endif
3865
3866 #if defined(__unix__)
3867 #define MA_UNIX
3868 #endif
3869 #if defined(__linux__)
3870 #define MA_LINUX
3871 #endif
3872 #if defined(__APPLE__)
3873 #define MA_APPLE
3874 #endif
3875 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
3876 #define MA_BSD
3877 #endif
3878 #if defined(__ANDROID__)
3879 #define MA_ANDROID
3880 #endif
3881 #if defined(__EMSCRIPTEN__)
3882 #define MA_EMSCRIPTEN
3883 #endif
3884 #if defined(__ORBIS__)
3885 #define MA_ORBIS
3886 #endif
3887 #if defined(__PROSPERO__)
3888 #define MA_PROSPERO
3889 #endif
3890 #if defined(__NX__)
3891 #define MA_NX
3892 #endif
3893 #if defined(__BEOS__) || defined(__HAIKU__)
3894 #define MA_BEOS
3895 #endif
3896 #if defined(__HAIKU__)
3897 #define MA_HAIKU
3898 #endif
3899 #endif
3900
3901 #if defined(__has_c_attribute)
3902 #if __has_c_attribute(fallthrough)
3903 #define MA_FALLTHROUGH [[fallthrough]]
3904 #endif
3905 #endif
3906 #if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__))
3907 #if __has_attribute(fallthrough)
3908 #define MA_FALLTHROUGH __attribute__((fallthrough))
3909 #endif
3910 #endif
3911 #if !defined(MA_FALLTHROUGH)
3912 #define MA_FALLTHROUGH ((void)0)
3913 #endif
3914
3915 #ifdef _MSC_VER
3916 #define MA_INLINE __forceinline
3917 #elif defined(__GNUC__)
3918 /*
3919 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
3920 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
3921 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
3922 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
3923 I am using "__inline__" only when we're compiling in strict ANSI mode.
3924 */
3925 #if defined(__STRICT_ANSI__)
3926 #define MA_GNUC_INLINE_HINT __inline__
3927 #else
3928 #define MA_GNUC_INLINE_HINT inline
3929 #endif
3930
3931 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
3932 #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline))
3933 #else
3934 #define MA_INLINE MA_GNUC_INLINE_HINT
3935 #endif
3936 #elif defined(__WATCOMC__)
3937 #define MA_INLINE __inline
3938 #else
3939 #define MA_INLINE
3940 #endif
3941
3942 #if !defined(MA_API)
3943 #if defined(MA_DLL)
3944 #if defined(_WIN32)
3945 #define MA_DLL_IMPORT __declspec(dllimport)
3946 #define MA_DLL_EXPORT __declspec(dllexport)
3947 #define MA_DLL_PRIVATE static
3948 #else
3949 #if defined(__GNUC__) && __GNUC__ >= 4
3950 #define MA_DLL_IMPORT __attribute__((visibility("default")))
3951 #define MA_DLL_EXPORT __attribute__((visibility("default")))
3952 #define MA_DLL_PRIVATE __attribute__((visibility("hidden")))
3953 #else
3954 #define MA_DLL_IMPORT
3955 #define MA_DLL_EXPORT
3956 #define MA_DLL_PRIVATE static
3957 #endif
3958 #endif
3959
3960 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
3961 #define MA_API MA_DLL_EXPORT
3962 #else
3963 #define MA_API MA_DLL_IMPORT
3964 #endif
3965 #define MA_PRIVATE MA_DLL_PRIVATE
3966 #else
3967 #define MA_API extern
3968 #define MA_PRIVATE static
3969 #endif
3970 #endif
3971
3972 /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */
3973 #define MA_SIMD_ALIGNMENT 32
3974
3975 /*
3976 Special wchar_t type to ensure any structures in the public sections that reference it have a
3977 consistent size across all platforms.
3978
3979 On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use
3980 wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all
3981 platforms.
3982 */
3983 #if !defined(MA_POSIX) && defined(MA_WIN32)
3984 typedef wchar_t ma_wchar_win32;
3985 #else
3986 typedef ma_uint16 ma_wchar_win32;
3987 #endif
3988
3989
3990
3991 /*
3992 Logging Levels
3993 ==============
3994 Log levels are only used to give logging callbacks some context as to the severity of a log message
3995 so they can do filtering. All log levels will be posted to registered logging callbacks. If you
3996 don't want to output a certain log level you can discriminate against the log level in the callback.
3997
3998 MA_LOG_LEVEL_DEBUG
3999 Used for debugging. Useful for debug and test builds, but should be disabled in release builds.
4000
4001 MA_LOG_LEVEL_INFO
4002 Informational logging. Useful for debugging. This will never be called from within the data
4003 callback.
4004
4005 MA_LOG_LEVEL_WARNING
4006 Warnings. You should enable this in you development builds and action them when encounted. These
4007 logs usually indicate a potential problem or misconfiguration, but still allow you to keep
4008 running. This will never be called from within the data callback.
4009
4010 MA_LOG_LEVEL_ERROR
4011 Error logging. This will be fired when an operation fails and is subsequently aborted. This can
4012 be fired from within the data callback, in which case the device will be stopped. You should
4013 always have this log level enabled.
4014 */
4015 typedef enum
4016 {
4017 MA_LOG_LEVEL_DEBUG = 4,
4018 MA_LOG_LEVEL_INFO = 3,
4019 MA_LOG_LEVEL_WARNING = 2,
4020 MA_LOG_LEVEL_ERROR = 1
4021 } ma_log_level;
4022
4023 /*
4024 Variables needing to be accessed atomically should be declared with this macro for two reasons:
4025
4026 1) It allows people who read the code to identify a variable as such; and
4027 2) It forces alignment on platforms where it's required or optimal.
4028
4029 Note that for x86/64, alignment is not strictly necessary, but does have some performance
4030 implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU
4031 architecture does not require it, it will simply leave it unaligned. This is the case with old
4032 versions of Visual Studio, which I've confirmed with at least VC6.
4033 */
4034 #if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
4035 #include <stdalign.h>
4036 #define MA_ATOMIC(alignment, type) _Alignas(alignment) type
4037 #else
4038 #if defined(__GNUC__)
4039 /* GCC-style compilers. */
4040 #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment)))
4041 #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */
4042 /* MSVC. */
4043 #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type
4044 #else
4045 /* Other compilers. */
4046 #define MA_ATOMIC(alignment, type) type
4047 #endif
4048 #endif
4049
4050 typedef struct ma_context ma_context;
4051 typedef struct ma_device ma_device;
4052
4053 typedef ma_uint8 ma_channel;
4054 typedef enum
4055 {
4056 MA_CHANNEL_NONE = 0,
4057 MA_CHANNEL_MONO = 1,
4058 MA_CHANNEL_FRONT_LEFT = 2,
4059 MA_CHANNEL_FRONT_RIGHT = 3,
4060 MA_CHANNEL_FRONT_CENTER = 4,
4061 MA_CHANNEL_LFE = 5,
4062 MA_CHANNEL_BACK_LEFT = 6,
4063 MA_CHANNEL_BACK_RIGHT = 7,
4064 MA_CHANNEL_FRONT_LEFT_CENTER = 8,
4065 MA_CHANNEL_FRONT_RIGHT_CENTER = 9,
4066 MA_CHANNEL_BACK_CENTER = 10,
4067 MA_CHANNEL_SIDE_LEFT = 11,
4068 MA_CHANNEL_SIDE_RIGHT = 12,
4069 MA_CHANNEL_TOP_CENTER = 13,
4070 MA_CHANNEL_TOP_FRONT_LEFT = 14,
4071 MA_CHANNEL_TOP_FRONT_CENTER = 15,
4072 MA_CHANNEL_TOP_FRONT_RIGHT = 16,
4073 MA_CHANNEL_TOP_BACK_LEFT = 17,
4074 MA_CHANNEL_TOP_BACK_CENTER = 18,
4075 MA_CHANNEL_TOP_BACK_RIGHT = 19,
4076 MA_CHANNEL_AUX_0 = 20,
4077 MA_CHANNEL_AUX_1 = 21,
4078 MA_CHANNEL_AUX_2 = 22,
4079 MA_CHANNEL_AUX_3 = 23,
4080 MA_CHANNEL_AUX_4 = 24,
4081 MA_CHANNEL_AUX_5 = 25,
4082 MA_CHANNEL_AUX_6 = 26,
4083 MA_CHANNEL_AUX_7 = 27,
4084 MA_CHANNEL_AUX_8 = 28,
4085 MA_CHANNEL_AUX_9 = 29,
4086 MA_CHANNEL_AUX_10 = 30,
4087 MA_CHANNEL_AUX_11 = 31,
4088 MA_CHANNEL_AUX_12 = 32,
4089 MA_CHANNEL_AUX_13 = 33,
4090 MA_CHANNEL_AUX_14 = 34,
4091 MA_CHANNEL_AUX_15 = 35,
4092 MA_CHANNEL_AUX_16 = 36,
4093 MA_CHANNEL_AUX_17 = 37,
4094 MA_CHANNEL_AUX_18 = 38,
4095 MA_CHANNEL_AUX_19 = 39,
4096 MA_CHANNEL_AUX_20 = 40,
4097 MA_CHANNEL_AUX_21 = 41,
4098 MA_CHANNEL_AUX_22 = 42,
4099 MA_CHANNEL_AUX_23 = 43,
4100 MA_CHANNEL_AUX_24 = 44,
4101 MA_CHANNEL_AUX_25 = 45,
4102 MA_CHANNEL_AUX_26 = 46,
4103 MA_CHANNEL_AUX_27 = 47,
4104 MA_CHANNEL_AUX_28 = 48,
4105 MA_CHANNEL_AUX_29 = 49,
4106 MA_CHANNEL_AUX_30 = 50,
4107 MA_CHANNEL_AUX_31 = 51,
4108 MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT,
4109 MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT,
4110 MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1)
4111 } _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */
4112
4113 typedef enum
4114 {
4115 MA_SUCCESS = 0,
4116 MA_ERROR = -1, /* A generic error. */
4117 MA_INVALID_ARGS = -2,
4118 MA_INVALID_OPERATION = -3,
4119 MA_OUT_OF_MEMORY = -4,
4120 MA_OUT_OF_RANGE = -5,
4121 MA_ACCESS_DENIED = -6,
4122 MA_DOES_NOT_EXIST = -7,
4123 MA_ALREADY_EXISTS = -8,
4124 MA_TOO_MANY_OPEN_FILES = -9,
4125 MA_INVALID_FILE = -10,
4126 MA_TOO_BIG = -11,
4127 MA_PATH_TOO_LONG = -12,
4128 MA_NAME_TOO_LONG = -13,
4129 MA_NOT_DIRECTORY = -14,
4130 MA_IS_DIRECTORY = -15,
4131 MA_DIRECTORY_NOT_EMPTY = -16,
4132 MA_AT_END = -17,
4133 MA_NO_SPACE = -18,
4134 MA_BUSY = -19,
4135 MA_IO_ERROR = -20,
4136 MA_INTERRUPT = -21,
4137 MA_UNAVAILABLE = -22,
4138 MA_ALREADY_IN_USE = -23,
4139 MA_BAD_ADDRESS = -24,
4140 MA_BAD_SEEK = -25,
4141 MA_BAD_PIPE = -26,
4142 MA_DEADLOCK = -27,
4143 MA_TOO_MANY_LINKS = -28,
4144 MA_NOT_IMPLEMENTED = -29,
4145 MA_NO_MESSAGE = -30,
4146 MA_BAD_MESSAGE = -31,
4147 MA_NO_DATA_AVAILABLE = -32,
4148 MA_INVALID_DATA = -33,
4149 MA_TIMEOUT = -34,
4150 MA_NO_NETWORK = -35,
4151 MA_NOT_UNIQUE = -36,
4152 MA_NOT_SOCKET = -37,
4153 MA_NO_ADDRESS = -38,
4154 MA_BAD_PROTOCOL = -39,
4155 MA_PROTOCOL_UNAVAILABLE = -40,
4156 MA_PROTOCOL_NOT_SUPPORTED = -41,
4157 MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42,
4158 MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43,
4159 MA_SOCKET_NOT_SUPPORTED = -44,
4160 MA_CONNECTION_RESET = -45,
4161 MA_ALREADY_CONNECTED = -46,
4162 MA_NOT_CONNECTED = -47,
4163 MA_CONNECTION_REFUSED = -48,
4164 MA_NO_HOST = -49,
4165 MA_IN_PROGRESS = -50,
4166 MA_CANCELLED = -51,
4167 MA_MEMORY_ALREADY_MAPPED = -52,
4168
4169 /* General miniaudio-specific errors. */
4170 MA_FORMAT_NOT_SUPPORTED = -100,
4171 MA_DEVICE_TYPE_NOT_SUPPORTED = -101,
4172 MA_SHARE_MODE_NOT_SUPPORTED = -102,
4173 MA_NO_BACKEND = -103,
4174 MA_NO_DEVICE = -104,
4175 MA_API_NOT_FOUND = -105,
4176 MA_INVALID_DEVICE_CONFIG = -106,
4177 MA_LOOP = -107,
4178 MA_BACKEND_NOT_ENABLED = -108,
4179
4180 /* State errors. */
4181 MA_DEVICE_NOT_INITIALIZED = -200,
4182 MA_DEVICE_ALREADY_INITIALIZED = -201,
4183 MA_DEVICE_NOT_STARTED = -202,
4184 MA_DEVICE_NOT_STOPPED = -203,
4185
4186 /* Operation errors. */
4187 MA_FAILED_TO_INIT_BACKEND = -300,
4188 MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301,
4189 MA_FAILED_TO_START_BACKEND_DEVICE = -302,
4190 MA_FAILED_TO_STOP_BACKEND_DEVICE = -303
4191 } ma_result;
4192
4193
4194 #define MA_MIN_CHANNELS 1
4195 #ifndef MA_MAX_CHANNELS
4196 #define MA_MAX_CHANNELS 254
4197 #endif
4198
4199 #ifndef MA_MAX_FILTER_ORDER
4200 #define MA_MAX_FILTER_ORDER 8
4201 #endif
4202
4203 typedef enum
4204 {
4205 ma_stream_format_pcm = 0
4206 } ma_stream_format;
4207
4208 typedef enum
4209 {
4210 ma_stream_layout_interleaved = 0,
4211 ma_stream_layout_deinterleaved
4212 } ma_stream_layout;
4213
4214 typedef enum
4215 {
4216 ma_dither_mode_none = 0,
4217 ma_dither_mode_rectangle,
4218 ma_dither_mode_triangle
4219 } ma_dither_mode;
4220
4221 typedef enum
4222 {
4223 /*
4224 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
4225 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
4226 */
4227 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
4228 ma_format_u8 = 1,
4229 ma_format_s16 = 2, /* Seems to be the most widely supported format. */
4230 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
4231 ma_format_s32 = 4,
4232 ma_format_f32 = 5,
4233 ma_format_count
4234 } ma_format;
4235
4236 typedef enum
4237 {
4238 /* Standard rates need to be in priority order. */
4239 ma_standard_sample_rate_48000 = 48000, /* Most common */
4240 ma_standard_sample_rate_44100 = 44100,
4241
4242 ma_standard_sample_rate_32000 = 32000, /* Lows */
4243 ma_standard_sample_rate_24000 = 24000,
4244 ma_standard_sample_rate_22050 = 22050,
4245
4246 ma_standard_sample_rate_88200 = 88200, /* Highs */
4247 ma_standard_sample_rate_96000 = 96000,
4248 ma_standard_sample_rate_176400 = 176400,
4249 ma_standard_sample_rate_192000 = 192000,
4250
4251 ma_standard_sample_rate_16000 = 16000, /* Extreme lows */
4252 ma_standard_sample_rate_11025 = 11250,
4253 ma_standard_sample_rate_8000 = 8000,
4254
4255 ma_standard_sample_rate_352800 = 352800, /* Extreme highs */
4256 ma_standard_sample_rate_384000 = 384000,
4257
4258 ma_standard_sample_rate_min = ma_standard_sample_rate_8000,
4259 ma_standard_sample_rate_max = ma_standard_sample_rate_384000,
4260 ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */
4261 } ma_standard_sample_rate;
4262
4263
4264 typedef enum
4265 {
4266 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
4267 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
4268 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */
4269 ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular
4270 } ma_channel_mix_mode;
4271
4272 typedef enum
4273 {
4274 ma_standard_channel_map_microsoft,
4275 ma_standard_channel_map_alsa,
4276 ma_standard_channel_map_rfc3551, /* Based off AIFF. */
4277 ma_standard_channel_map_flac,
4278 ma_standard_channel_map_vorbis,
4279 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
4280 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
4281 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. */
4282 ma_standard_channel_map_default = ma_standard_channel_map_microsoft
4283 } ma_standard_channel_map;
4284
4285 typedef enum
4286 {
4287 ma_performance_profile_low_latency = 0,
4288 ma_performance_profile_conservative
4289 } ma_performance_profile;
4290
4291
4292 typedef struct
4293 {
4294 void* pUserData;
4295 void* (* onMalloc)(size_t sz, void* pUserData);
4296 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
4297 void (* onFree)(void* p, void* pUserData);
4298 } ma_allocation_callbacks;
4299
4300 typedef struct
4301 {
4302 ma_int32 state;
4303 } ma_lcg;
4304
4305
4306 /*
4307 Atomics.
4308
4309 These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too
4310 easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By
4311 using a struct we can enforce the use of atomics at compile time.
4312
4313 These types are declared in the header section because we need to reference them in structs below, but functions for
4314 using them are only exposed in the implementation section. I do not want these to be part of the public API.
4315
4316 There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are
4317 some macros to help with the declarations. They will be named like so:
4318
4319 ma_atomic_uint32 - atomic ma_uint32
4320 ma_atomic_int32 - atomic ma_int32
4321 ma_atomic_uint64 - atomic ma_uint64
4322 ma_atomic_float - atomic float
4323 ma_atomic_bool32 - atomic ma_bool32
4324
4325 The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific
4326 type of pointer you need to make atomic. For example, an atomic ma_node* will look like this:
4327
4328 MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node)
4329
4330 Which will declare a type struct that's named like so:
4331
4332 ma_atomic_ptr_node
4333
4334 Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with
4335 the name of the struct. For example:
4336
4337 ma_atomic_uint32_set() - Atomic store of ma_uint32
4338 ma_atomic_uint32_get() - Atomic load of ma_uint32
4339 etc.
4340
4341 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
4342 return you get type safety and enforcement of atomic operations.
4343 */
4344 #define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \
4345 typedef struct \
4346 { \
4347 MA_ATOMIC(typeSize, ma_##type) value; \
4348 } ma_atomic_##type; \
4349
4350 #define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \
4351 typedef struct \
4352 { \
4353 MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \
4354 } ma_atomic_ptr_##type; \
4355
4356 MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32)
4357 MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32)
4358 MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64)
4359 MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float)
4360 MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32)
4361
4362
4363 /* Spinlocks are 32-bit for compatibility reasons. */
4364 typedef ma_uint32 ma_spinlock;
4365
4366 #ifndef MA_NO_THREADING
4367 /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */
4368 typedef enum
4369 {
4370 ma_thread_priority_idle = -5,
4371 ma_thread_priority_lowest = -4,
4372 ma_thread_priority_low = -3,
4373 ma_thread_priority_normal = -2,
4374 ma_thread_priority_high = -1,
4375 ma_thread_priority_highest = 0,
4376 ma_thread_priority_realtime = 1,
4377 ma_thread_priority_default = 0
4378 } ma_thread_priority;
4379
4380 #if defined(MA_POSIX)
4381 typedef ma_pthread_t ma_thread;
4382 #elif defined(MA_WIN32)
4383 typedef ma_handle ma_thread;
4384 #endif
4385
4386 #if defined(MA_POSIX)
4387 typedef ma_pthread_mutex_t ma_mutex;
4388 #elif defined(MA_WIN32)
4389 typedef ma_handle ma_mutex;
4390 #endif
4391
4392 #if defined(MA_POSIX)
4393 typedef struct
4394 {
4395 ma_uint32 value;
4396 ma_pthread_mutex_t lock;
4397 ma_pthread_cond_t cond;
4398 } ma_event;
4399 #elif defined(MA_WIN32)
4400 typedef ma_handle ma_event;
4401 #endif
4402
4403 #if defined(MA_POSIX)
4404 typedef struct
4405 {
4406 int value;
4407 ma_pthread_mutex_t lock;
4408 ma_pthread_cond_t cond;
4409 } ma_semaphore;
4410 #elif defined(MA_WIN32)
4411 typedef ma_handle ma_semaphore;
4412 #endif
4413 #else
4414 /* 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. */
4415 #ifndef MA_NO_DEVICE_IO
4416 #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
4417 #endif
4418 #endif /* MA_NO_THREADING */
4419
4420
4421 /*
4422 Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.
4423 */
4424 MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
4425
4426 /*
4427 Retrieves the version of miniaudio as a string which can be useful for logging purposes.
4428 */
4429 MA_API const char* ma_version_string(void);
4430
4431
4432 /**************************************************************************************************************************************************************
4433
4434 Logging
4435
4436 **************************************************************************************************************************************************************/
4437 #include <stdarg.h> /* For va_list. */
4438
4439 #if defined(__has_attribute)
4440 #if __has_attribute(format)
4441 #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))
4442 #endif
4443 #endif
4444 #ifndef MA_ATTRIBUTE_FORMAT
4445 #define MA_ATTRIBUTE_FORMAT(fmt, va)
4446 #endif
4447
4448 #ifndef MA_MAX_LOG_CALLBACKS
4449 #define MA_MAX_LOG_CALLBACKS 4
4450 #endif
4451
4452
4453 /*
4454 The callback for handling log messages.
4455
4456
4457 Parameters
4458 ----------
4459 pUserData (in)
4460 The user data pointer that was passed into ma_log_register_callback().
4461
4462 logLevel (in)
4463 The log level. This can be one of the following:
4464
4465 +----------------------+
4466 | Log Level |
4467 +----------------------+
4468 | MA_LOG_LEVEL_DEBUG |
4469 | MA_LOG_LEVEL_INFO |
4470 | MA_LOG_LEVEL_WARNING |
4471 | MA_LOG_LEVEL_ERROR |
4472 +----------------------+
4473
4474 pMessage (in)
4475 The log message.
4476 */
4477 typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage);
4478
4479 typedef struct
4480 {
4481 ma_log_callback_proc onLog;
4482 void* pUserData;
4483 } ma_log_callback;
4484
4485 MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData);
4486
4487
4488 typedef struct
4489 {
4490 ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS];
4491 ma_uint32 callbackCount;
4492 ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */
4493 #ifndef MA_NO_THREADING
4494 ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */
4495 #endif
4496 } ma_log;
4497
4498 MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog);
4499 MA_API void ma_log_uninit(ma_log* pLog);
4500 MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback);
4501 MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback);
4502 MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage);
4503 MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args);
4504 MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4);
4505
4506
4507 /**************************************************************************************************************************************************************
4508
4509 Biquad Filtering
4510
4511 **************************************************************************************************************************************************************/
4512 typedef union
4513 {
4514 float f32;
4515 ma_int32 s32;
4516 } ma_biquad_coefficient;
4517
4518 typedef struct
4519 {
4520 ma_format format;
4521 ma_uint32 channels;
4522 double b0;
4523 double b1;
4524 double b2;
4525 double a0;
4526 double a1;
4527 double a2;
4528 } ma_biquad_config;
4529
4530 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);
4531
4532 typedef struct
4533 {
4534 ma_format format;
4535 ma_uint32 channels;
4536 ma_biquad_coefficient b0;
4537 ma_biquad_coefficient b1;
4538 ma_biquad_coefficient b2;
4539 ma_biquad_coefficient a1;
4540 ma_biquad_coefficient a2;
4541 ma_biquad_coefficient* pR1;
4542 ma_biquad_coefficient* pR2;
4543
4544 /* Memory management. */
4545 void* _pHeap;
4546 ma_bool32 _ownsHeap;
4547 } ma_biquad;
4548
4549 MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes);
4550 MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ);
4551 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ);
4552 MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks);
4553 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
4554 MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ);
4555 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4556 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ);
4557
4558
4559 /**************************************************************************************************************************************************************
4560
4561 Low-Pass Filtering
4562
4563 **************************************************************************************************************************************************************/
4564 typedef struct
4565 {
4566 ma_format format;
4567 ma_uint32 channels;
4568 ma_uint32 sampleRate;
4569 double cutoffFrequency;
4570 double q;
4571 } ma_lpf1_config, ma_lpf2_config;
4572
4573 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
4574 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
4575
4576 typedef struct
4577 {
4578 ma_format format;
4579 ma_uint32 channels;
4580 ma_biquad_coefficient a;
4581 ma_biquad_coefficient* pR1;
4582
4583 /* Memory management. */
4584 void* _pHeap;
4585 ma_bool32 _ownsHeap;
4586 } ma_lpf1;
4587
4588 MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes);
4589 MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF);
4590 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF);
4591 MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4592 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
4593 MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF);
4594 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4595 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF);
4596
4597 typedef struct
4598 {
4599 ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */
4600 } ma_lpf2;
4601
4602 MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes);
4603 MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF);
4604 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF);
4605 MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4606 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
4607 MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF);
4608 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4609 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF);
4610
4611
4612 typedef struct
4613 {
4614 ma_format format;
4615 ma_uint32 channels;
4616 ma_uint32 sampleRate;
4617 double cutoffFrequency;
4618 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4619 } ma_lpf_config;
4620
4621 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
4622
4623 typedef struct
4624 {
4625 ma_format format;
4626 ma_uint32 channels;
4627 ma_uint32 sampleRate;
4628 ma_uint32 lpf1Count;
4629 ma_uint32 lpf2Count;
4630 ma_lpf1* pLPF1;
4631 ma_lpf2* pLPF2;
4632
4633 /* Memory management. */
4634 void* _pHeap;
4635 ma_bool32 _ownsHeap;
4636 } ma_lpf;
4637
4638 MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes);
4639 MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF);
4640 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF);
4641 MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4642 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
4643 MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF);
4644 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4645 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF);
4646
4647
4648 /**************************************************************************************************************************************************************
4649
4650 High-Pass Filtering
4651
4652 **************************************************************************************************************************************************************/
4653 typedef struct
4654 {
4655 ma_format format;
4656 ma_uint32 channels;
4657 ma_uint32 sampleRate;
4658 double cutoffFrequency;
4659 double q;
4660 } ma_hpf1_config, ma_hpf2_config;
4661
4662 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
4663 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
4664
4665 typedef struct
4666 {
4667 ma_format format;
4668 ma_uint32 channels;
4669 ma_biquad_coefficient a;
4670 ma_biquad_coefficient* pR1;
4671
4672 /* Memory management. */
4673 void* _pHeap;
4674 ma_bool32 _ownsHeap;
4675 } ma_hpf1;
4676
4677 MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes);
4678 MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF);
4679 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF);
4680 MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4681 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
4682 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4683 MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF);
4684
4685 typedef struct
4686 {
4687 ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */
4688 } ma_hpf2;
4689
4690 MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes);
4691 MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF);
4692 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF);
4693 MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4694 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
4695 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4696 MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF);
4697
4698
4699 typedef struct
4700 {
4701 ma_format format;
4702 ma_uint32 channels;
4703 ma_uint32 sampleRate;
4704 double cutoffFrequency;
4705 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4706 } ma_hpf_config;
4707
4708 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
4709
4710 typedef struct
4711 {
4712 ma_format format;
4713 ma_uint32 channels;
4714 ma_uint32 sampleRate;
4715 ma_uint32 hpf1Count;
4716 ma_uint32 hpf2Count;
4717 ma_hpf1* pHPF1;
4718 ma_hpf2* pHPF2;
4719
4720 /* Memory management. */
4721 void* _pHeap;
4722 ma_bool32 _ownsHeap;
4723 } ma_hpf;
4724
4725 MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes);
4726 MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF);
4727 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF);
4728 MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4729 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF);
4730 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4731 MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF);
4732
4733
4734 /**************************************************************************************************************************************************************
4735
4736 Band-Pass Filtering
4737
4738 **************************************************************************************************************************************************************/
4739 typedef struct
4740 {
4741 ma_format format;
4742 ma_uint32 channels;
4743 ma_uint32 sampleRate;
4744 double cutoffFrequency;
4745 double q;
4746 } ma_bpf2_config;
4747
4748 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
4749
4750 typedef struct
4751 {
4752 ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */
4753 } ma_bpf2;
4754
4755 MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes);
4756 MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF);
4757 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF);
4758 MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
4759 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
4760 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4761 MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF);
4762
4763
4764 typedef struct
4765 {
4766 ma_format format;
4767 ma_uint32 channels;
4768 ma_uint32 sampleRate;
4769 double cutoffFrequency;
4770 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4771 } ma_bpf_config;
4772
4773 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
4774
4775 typedef struct
4776 {
4777 ma_format format;
4778 ma_uint32 channels;
4779 ma_uint32 bpf2Count;
4780 ma_bpf2* pBPF2;
4781
4782 /* Memory management. */
4783 void* _pHeap;
4784 ma_bool32 _ownsHeap;
4785 } ma_bpf;
4786
4787 MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes);
4788 MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF);
4789 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF);
4790 MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
4791 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF);
4792 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4793 MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF);
4794
4795
4796 /**************************************************************************************************************************************************************
4797
4798 Notching Filter
4799
4800 **************************************************************************************************************************************************************/
4801 typedef struct
4802 {
4803 ma_format format;
4804 ma_uint32 channels;
4805 ma_uint32 sampleRate;
4806 double q;
4807 double frequency;
4808 } ma_notch2_config, ma_notch_config;
4809
4810 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
4811
4812 typedef struct
4813 {
4814 ma_biquad bq;
4815 } ma_notch2;
4816
4817 MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes);
4818 MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter);
4819 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter);
4820 MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4821 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter);
4822 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4823 MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter);
4824
4825
4826 /**************************************************************************************************************************************************************
4827
4828 Peaking EQ Filter
4829
4830 **************************************************************************************************************************************************************/
4831 typedef struct
4832 {
4833 ma_format format;
4834 ma_uint32 channels;
4835 ma_uint32 sampleRate;
4836 double gainDB;
4837 double q;
4838 double frequency;
4839 } ma_peak2_config, ma_peak_config;
4840
4841 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
4842
4843 typedef struct
4844 {
4845 ma_biquad bq;
4846 } ma_peak2;
4847
4848 MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes);
4849 MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter);
4850 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter);
4851 MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4852 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter);
4853 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4854 MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter);
4855
4856
4857 /**************************************************************************************************************************************************************
4858
4859 Low Shelf Filter
4860
4861 **************************************************************************************************************************************************************/
4862 typedef struct
4863 {
4864 ma_format format;
4865 ma_uint32 channels;
4866 ma_uint32 sampleRate;
4867 double gainDB;
4868 double shelfSlope;
4869 double frequency;
4870 } ma_loshelf2_config, ma_loshelf_config;
4871
4872 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
4873
4874 typedef struct
4875 {
4876 ma_biquad bq;
4877 } ma_loshelf2;
4878
4879 MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes);
4880 MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter);
4881 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter);
4882 MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4883 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
4884 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4885 MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter);
4886
4887
4888 /**************************************************************************************************************************************************************
4889
4890 High Shelf Filter
4891
4892 **************************************************************************************************************************************************************/
4893 typedef struct
4894 {
4895 ma_format format;
4896 ma_uint32 channels;
4897 ma_uint32 sampleRate;
4898 double gainDB;
4899 double shelfSlope;
4900 double frequency;
4901 } ma_hishelf2_config, ma_hishelf_config;
4902
4903 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
4904
4905 typedef struct
4906 {
4907 ma_biquad bq;
4908 } ma_hishelf2;
4909
4910 MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes);
4911 MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter);
4912 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter);
4913 MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4914 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
4915 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4916 MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter);
4917
4918
4919
4920 /*
4921 Delay
4922 */
4923 typedef struct
4924 {
4925 ma_uint32 channels;
4926 ma_uint32 sampleRate;
4927 ma_uint32 delayInFrames;
4928 ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */
4929 float wet; /* 0..1. Default = 1. */
4930 float dry; /* 0..1. Default = 1. */
4931 float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */
4932 } ma_delay_config;
4933
4934 MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
4935
4936
4937 typedef struct
4938 {
4939 ma_delay_config config;
4940 ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */
4941 ma_uint32 bufferSizeInFrames;
4942 float* pBuffer;
4943 } ma_delay;
4944
4945 MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay);
4946 MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks);
4947 MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount);
4948 MA_API void ma_delay_set_wet(ma_delay* pDelay, float value);
4949 MA_API float ma_delay_get_wet(const ma_delay* pDelay);
4950 MA_API void ma_delay_set_dry(ma_delay* pDelay, float value);
4951 MA_API float ma_delay_get_dry(const ma_delay* pDelay);
4952 MA_API void ma_delay_set_decay(ma_delay* pDelay, float value);
4953 MA_API float ma_delay_get_decay(const ma_delay* pDelay);
4954
4955
4956 /* Gainer for smooth volume changes. */
4957 typedef struct
4958 {
4959 ma_uint32 channels;
4960 ma_uint32 smoothTimeInFrames;
4961 } ma_gainer_config;
4962
4963 MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames);
4964
4965
4966 typedef struct
4967 {
4968 ma_gainer_config config;
4969 ma_uint32 t;
4970 float masterVolume;
4971 float* pOldGains;
4972 float* pNewGains;
4973
4974 /* Memory management. */
4975 void* _pHeap;
4976 ma_bool32 _ownsHeap;
4977 } ma_gainer;
4978
4979 MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes);
4980 MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer);
4981 MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer);
4982 MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks);
4983 MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4984 MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain);
4985 MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains);
4986 MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume);
4987 MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume);
4988
4989
4990
4991 /* Stereo panner. */
4992 typedef enum
4993 {
4994 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. */
4995 ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */
4996 } ma_pan_mode;
4997
4998 typedef struct
4999 {
5000 ma_format format;
5001 ma_uint32 channels;
5002 ma_pan_mode mode;
5003 float pan;
5004 } ma_panner_config;
5005
5006 MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels);
5007
5008
5009 typedef struct
5010 {
5011 ma_format format;
5012 ma_uint32 channels;
5013 ma_pan_mode mode;
5014 float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */
5015 } ma_panner;
5016
5017 MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner);
5018 MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5019 MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode);
5020 MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner);
5021 MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan);
5022 MA_API float ma_panner_get_pan(const ma_panner* pPanner);
5023
5024
5025
5026 /* Fader. */
5027 typedef struct
5028 {
5029 ma_format format;
5030 ma_uint32 channels;
5031 ma_uint32 sampleRate;
5032 } ma_fader_config;
5033
5034 MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
5035
5036 typedef struct
5037 {
5038 ma_fader_config config;
5039 float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */
5040 float volumeEnd;
5041 ma_uint64 lengthInFrames; /* The total length of the fade. */
5042 ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */
5043 } ma_fader;
5044
5045 MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader);
5046 MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5047 MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
5048 MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames);
5049 MA_API float ma_fader_get_current_volume(const ma_fader* pFader);
5050
5051
5052
5053 /* Spatializer. */
5054 typedef struct
5055 {
5056 float x;
5057 float y;
5058 float z;
5059 } ma_vec3f;
5060
5061 typedef struct
5062 {
5063 ma_vec3f v;
5064 ma_spinlock lock;
5065 } ma_atomic_vec3f;
5066
5067 typedef enum
5068 {
5069 ma_attenuation_model_none, /* No distance attenuation and no spatialization. */
5070 ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */
5071 ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */
5072 ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */
5073 } ma_attenuation_model;
5074
5075 typedef enum
5076 {
5077 ma_positioning_absolute,
5078 ma_positioning_relative
5079 } ma_positioning;
5080
5081 typedef enum
5082 {
5083 ma_handedness_right,
5084 ma_handedness_left
5085 } ma_handedness;
5086
5087
5088 typedef struct
5089 {
5090 ma_uint32 channelsOut;
5091 ma_channel* pChannelMapOut;
5092 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. */
5093 float coneInnerAngleInRadians;
5094 float coneOuterAngleInRadians;
5095 float coneOuterGain;
5096 float speedOfSound;
5097 ma_vec3f worldUp;
5098 } ma_spatializer_listener_config;
5099
5100 MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut);
5101
5102
5103 typedef struct
5104 {
5105 ma_spatializer_listener_config config;
5106 ma_atomic_vec3f position; /* The absolute position of the listener. */
5107 ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */
5108 ma_atomic_vec3f velocity;
5109 ma_bool32 isEnabled;
5110
5111 /* Memory management. */
5112 ma_bool32 _ownsHeap;
5113 void* _pHeap;
5114 } ma_spatializer_listener;
5115
5116 MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes);
5117 MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener);
5118 MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener);
5119 MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks);
5120 MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener);
5121 MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
5122 MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
5123 MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z);
5124 MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener);
5125 MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z);
5126 MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener);
5127 MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z);
5128 MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener);
5129 MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound);
5130 MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener);
5131 MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z);
5132 MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener);
5133 MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled);
5134 MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener);
5135
5136
5137 typedef struct
5138 {
5139 ma_uint32 channelsIn;
5140 ma_uint32 channelsOut;
5141 ma_channel* pChannelMapIn;
5142 ma_attenuation_model attenuationModel;
5143 ma_positioning positioning;
5144 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. */
5145 float minGain;
5146 float maxGain;
5147 float minDistance;
5148 float maxDistance;
5149 float rolloff;
5150 float coneInnerAngleInRadians;
5151 float coneOuterAngleInRadians;
5152 float coneOuterGain;
5153 float dopplerFactor; /* Set to 0 to disable doppler effect. */
5154 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
5155 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. */
5156 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
5157 } ma_spatializer_config;
5158
5159 MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut);
5160
5161
5162 typedef struct
5163 {
5164 ma_uint32 channelsIn;
5165 ma_uint32 channelsOut;
5166 ma_channel* pChannelMapIn;
5167 ma_attenuation_model attenuationModel;
5168 ma_positioning positioning;
5169 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. */
5170 float minGain;
5171 float maxGain;
5172 float minDistance;
5173 float maxDistance;
5174 float rolloff;
5175 float coneInnerAngleInRadians;
5176 float coneOuterAngleInRadians;
5177 float coneOuterGain;
5178 float dopplerFactor; /* Set to 0 to disable doppler effect. */
5179 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
5180 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
5181 ma_atomic_vec3f position;
5182 ma_atomic_vec3f direction;
5183 ma_atomic_vec3f velocity; /* For doppler effect. */
5184 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. */
5185 float minSpatializationChannelGain;
5186 ma_gainer gainer; /* For smooth gain transitions. */
5187 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. */
5188
5189 /* Memory management. */
5190 void* _pHeap;
5191 ma_bool32 _ownsHeap;
5192 } ma_spatializer;
5193
5194 MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes);
5195 MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer);
5196 MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer);
5197 MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks);
5198 MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5199 MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume);
5200 MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume);
5201 MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer);
5202 MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer);
5203 MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel);
5204 MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer);
5205 MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning);
5206 MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer);
5207 MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff);
5208 MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer);
5209 MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain);
5210 MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer);
5211 MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain);
5212 MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer);
5213 MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance);
5214 MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer);
5215 MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance);
5216 MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer);
5217 MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
5218 MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
5219 MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor);
5220 MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer);
5221 MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor);
5222 MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer);
5223 MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z);
5224 MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer);
5225 MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z);
5226 MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer);
5227 MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z);
5228 MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer);
5229 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);
5230
5231
5232
5233 /************************************************************************************************************************************************************
5234 *************************************************************************************************************************************************************
5235
5236 DATA CONVERSION
5237 ===============
5238
5239 This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
5240
5241 *************************************************************************************************************************************************************
5242 ************************************************************************************************************************************************************/
5243
5244 /**************************************************************************************************************************************************************
5245
5246 Resampling
5247
5248 **************************************************************************************************************************************************************/
5249 typedef struct
5250 {
5251 ma_format format;
5252 ma_uint32 channels;
5253 ma_uint32 sampleRateIn;
5254 ma_uint32 sampleRateOut;
5255 ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */
5256 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
5257 } ma_linear_resampler_config;
5258
5259 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
5260
5261 typedef struct
5262 {
5263 ma_linear_resampler_config config;
5264 ma_uint32 inAdvanceInt;
5265 ma_uint32 inAdvanceFrac;
5266 ma_uint32 inTimeInt;
5267 ma_uint32 inTimeFrac;
5268 union
5269 {
5270 float* f32;
5271 ma_int16* s16;
5272 } x0; /* The previous input frame. */
5273 union
5274 {
5275 float* f32;
5276 ma_int16* s16;
5277 } x1; /* The next input frame. */
5278 ma_lpf lpf;
5279
5280 /* Memory management. */
5281 void* _pHeap;
5282 ma_bool32 _ownsHeap;
5283 } ma_linear_resampler;
5284
5285 MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes);
5286 MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler);
5287 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler);
5288 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);
5289 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);
5290 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
5291 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);
5292 MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler);
5293 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler);
5294 MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
5295 MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
5296 MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler);
5297
5298
5299 typedef struct ma_resampler_config ma_resampler_config;
5300
5301 typedef void ma_resampling_backend;
5302 typedef struct
5303 {
5304 ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
5305 ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend);
5306 void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
5307 ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5308 ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */
5309 ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
5310 ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
5311 ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */
5312 ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */
5313 ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend);
5314 } ma_resampling_backend_vtable;
5315
5316 typedef enum
5317 {
5318 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */
5319 ma_resample_algorithm_custom,
5320 } ma_resample_algorithm;
5321
5322 struct ma_resampler_config
5323 {
5324 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */
5325 ma_uint32 channels;
5326 ma_uint32 sampleRateIn;
5327 ma_uint32 sampleRateOut;
5328 ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */
5329 ma_resampling_backend_vtable* pBackendVTable;
5330 void* pBackendUserData;
5331 struct
5332 {
5333 ma_uint32 lpfOrder;
5334 } linear;
5335 };
5336
5337 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);
5338
5339 typedef struct
5340 {
5341 ma_resampling_backend* pBackend;
5342 ma_resampling_backend_vtable* pBackendVTable;
5343 void* pBackendUserData;
5344 ma_format format;
5345 ma_uint32 channels;
5346 ma_uint32 sampleRateIn;
5347 ma_uint32 sampleRateOut;
5348 union
5349 {
5350 ma_linear_resampler linear;
5351 } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */
5352
5353 /* Memory management. */
5354 void* _pHeap;
5355 ma_bool32 _ownsHeap;
5356 } ma_resampler;
5357
5358 MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
5359 MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler);
5360
5361 /*
5362 Initializes a new resampler object from a config.
5363 */
5364 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler);
5365
5366 /*
5367 Uninitializes a resampler.
5368 */
5369 MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);
5370
5371 /*
5372 Converts the given input data.
5373
5374 Both the input and output frames must be in the format specified in the config when the resampler was initilized.
5375
5376 On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
5377 were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
5378 ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
5379
5380 On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
5381 input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
5382 you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
5383
5384 If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
5385 output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
5386 frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
5387 processed. In this case, any internal filter state will be updated as if zeroes were passed in.
5388
5389 It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
5390
5391 It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
5392 */
5393 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5394
5395
5396 /*
5397 Sets the input and output sample rate.
5398 */
5399 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
5400
5401 /*
5402 Sets the input and output sample rate as a ratio.
5403
5404 The ration is in/out.
5405 */
5406 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);
5407
5408 /*
5409 Retrieves the latency introduced by the resampler in input frames.
5410 */
5411 MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler);
5412
5413 /*
5414 Retrieves the latency introduced by the resampler in output frames.
5415 */
5416 MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler);
5417
5418 /*
5419 Calculates the number of whole input frames that would need to be read from the client in order to output the specified
5420 number of output frames.
5421
5422 The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
5423 read from the input buffer in order to output the specified number of output frames.
5424 */
5425 MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
5426
5427 /*
5428 Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
5429 input frames.
5430 */
5431 MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
5432
5433 /*
5434 Resets the resampler's timer and clears it's internal cache.
5435 */
5436 MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);
5437
5438
5439 /**************************************************************************************************************************************************************
5440
5441 Channel Conversion
5442
5443 **************************************************************************************************************************************************************/
5444 typedef enum
5445 {
5446 ma_channel_conversion_path_unknown,
5447 ma_channel_conversion_path_passthrough,
5448 ma_channel_conversion_path_mono_out, /* Converting to mono. */
5449 ma_channel_conversion_path_mono_in, /* Converting from mono. */
5450 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. */
5451 ma_channel_conversion_path_weights /* Blended based on weights. */
5452 } ma_channel_conversion_path;
5453
5454 typedef enum
5455 {
5456 ma_mono_expansion_mode_duplicate = 0, /* The default. */
5457 ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */
5458 ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */
5459 ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate
5460 } ma_mono_expansion_mode;
5461
5462 typedef struct
5463 {
5464 ma_format format;
5465 ma_uint32 channelsIn;
5466 ma_uint32 channelsOut;
5467 const ma_channel* pChannelMapIn;
5468 const ma_channel* pChannelMapOut;
5469 ma_channel_mix_mode mixingMode;
5470 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. */
5471 float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
5472 } ma_channel_converter_config;
5473
5474 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);
5475
5476 typedef struct
5477 {
5478 ma_format format;
5479 ma_uint32 channelsIn;
5480 ma_uint32 channelsOut;
5481 ma_channel_mix_mode mixingMode;
5482 ma_channel_conversion_path conversionPath;
5483 ma_channel* pChannelMapIn;
5484 ma_channel* pChannelMapOut;
5485 ma_uint8* pShuffleTable; /* Indexed by output channel index. */
5486 union
5487 {
5488 float** f32;
5489 ma_int32** s16;
5490 } weights; /* [in][out] */
5491
5492 /* Memory management. */
5493 void* _pHeap;
5494 ma_bool32 _ownsHeap;
5495 } ma_channel_converter;
5496
5497 MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes);
5498 MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter);
5499 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter);
5500 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
5501 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5502 MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5503 MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5504
5505
5506 /**************************************************************************************************************************************************************
5507
5508 Data Conversion
5509
5510 **************************************************************************************************************************************************************/
5511 typedef struct
5512 {
5513 ma_format formatIn;
5514 ma_format formatOut;
5515 ma_uint32 channelsIn;
5516 ma_uint32 channelsOut;
5517 ma_uint32 sampleRateIn;
5518 ma_uint32 sampleRateOut;
5519 ma_channel* pChannelMapIn;
5520 ma_channel* pChannelMapOut;
5521 ma_dither_mode ditherMode;
5522 ma_channel_mix_mode channelMixMode;
5523 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. */
5524 float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
5525 ma_bool32 allowDynamicSampleRate;
5526 ma_resampler_config resampling;
5527 } ma_data_converter_config;
5528
5529 MA_API ma_data_converter_config ma_data_converter_config_init_default(void);
5530 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);
5531
5532
5533 typedef enum
5534 {
5535 ma_data_converter_execution_path_passthrough, /* No conversion. */
5536 ma_data_converter_execution_path_format_only, /* Only format conversion. */
5537 ma_data_converter_execution_path_channels_only, /* Only channel conversion. */
5538 ma_data_converter_execution_path_resample_only, /* Only resampling. */
5539 ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */
5540 ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */
5541 } ma_data_converter_execution_path;
5542
5543 typedef struct
5544 {
5545 ma_format formatIn;
5546 ma_format formatOut;
5547 ma_uint32 channelsIn;
5548 ma_uint32 channelsOut;
5549 ma_uint32 sampleRateIn;
5550 ma_uint32 sampleRateOut;
5551 ma_dither_mode ditherMode;
5552 ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */
5553 ma_channel_converter channelConverter;
5554 ma_resampler resampler;
5555 ma_bool8 hasPreFormatConversion;
5556 ma_bool8 hasPostFormatConversion;
5557 ma_bool8 hasChannelConverter;
5558 ma_bool8 hasResampler;
5559 ma_bool8 isPassthrough;
5560
5561 /* Memory management. */
5562 ma_bool8 _ownsHeap;
5563 void* _pHeap;
5564 } ma_data_converter;
5565
5566 MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes);
5567 MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter);
5568 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter);
5569 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
5570 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);
5571 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
5572 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);
5573 MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter);
5574 MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter);
5575 MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
5576 MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
5577 MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5578 MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5579 MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter);
5580
5581
5582 /************************************************************************************************************************************************************
5583
5584 Format Conversion
5585
5586 ************************************************************************************************************************************************************/
5587 MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5588 MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5589 MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5590 MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5591 MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5592 MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5593 MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5594 MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5595 MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5596 MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5597 MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5598 MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5599 MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5600 MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5601 MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5602 MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5603 MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5604 MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5605 MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5606 MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5607 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
5608 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);
5609
5610 /*
5611 Deinterleaves an interleaved buffer.
5612 */
5613 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
5614
5615 /*
5616 Interleaves a group of deinterleaved buffers.
5617 */
5618 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
5619
5620
5621 /************************************************************************************************************************************************************
5622
5623 Channel Maps
5624
5625 ************************************************************************************************************************************************************/
5626 /*
5627 This is used in the shuffle table to indicate that the channel index is undefined and should be ignored.
5628 */
5629 #define MA_CHANNEL_INDEX_NULL 255
5630
5631 /*
5632 Retrieves the channel position of the specified channel in the given channel map.
5633
5634 The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed.
5635 */
5636 MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
5637
5638 /*
5639 Initializes a blank channel map.
5640
5641 When a blank channel map is specified anywhere it indicates that the native channel map should be used.
5642 */
5643 MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels);
5644
5645 /*
5646 Helper for retrieving a standard channel map.
5647
5648 The output channel map buffer must have a capacity of at least `channelMapCap`.
5649 */
5650 MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels);
5651
5652 /*
5653 Copies a channel map.
5654
5655 Both input and output channel map buffers must have a capacity of at at least `channels`.
5656 */
5657 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
5658
5659 /*
5660 Copies a channel map if one is specified, otherwise copies the default channel map.
5661
5662 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`.
5663 */
5664 MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels);
5665
5666
5667 /*
5668 Determines whether or not a channel map is valid.
5669
5670 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
5671 is usually treated as a passthrough.
5672
5673 Invalid channel maps:
5674 - A channel map with no channels
5675 - A channel map with more than one channel and a mono channel
5676
5677 The channel map buffer must have a capacity of at least `channels`.
5678 */
5679 MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels);
5680
5681 /*
5682 Helper for comparing two channel maps for equality.
5683
5684 This assumes the channel count is the same between the two.
5685
5686 Both channels map buffers must have a capacity of at least `channels`.
5687 */
5688 MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels);
5689
5690 /*
5691 Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
5692
5693 The channel map buffer must have a capacity of at least `channels`.
5694 */
5695 MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels);
5696
5697 /*
5698 Helper for determining whether or not a channel is present in the given channel map.
5699
5700 The channel map buffer must have a capacity of at least `channels`.
5701 */
5702 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition);
5703
5704 /*
5705 Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The
5706 index of the channel is output to `pChannelIndex`.
5707
5708 The channel map buffer must have a capacity of at least `channels`.
5709 */
5710 MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex);
5711
5712 /*
5713 Generates a string representing the given channel map.
5714
5715 This is for printing and debugging purposes, not serialization/deserialization.
5716
5717 Returns the length of the string, not including the null terminator.
5718 */
5719 MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap);
5720
5721 /*
5722 Retrieves a human readable version of a channel position.
5723 */
5724 MA_API const char* ma_channel_position_to_string(ma_channel channel);
5725
5726
5727 /************************************************************************************************************************************************************
5728
5729 Conversion Helpers
5730
5731 ************************************************************************************************************************************************************/
5732
5733 /*
5734 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
5735 determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
5736 ignored.
5737
5738 A return value of 0 indicates an error.
5739
5740 This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.
5741 */
5742 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);
5743 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);
5744
5745
5746 /************************************************************************************************************************************************************
5747
5748 Data Source
5749
5750 ************************************************************************************************************************************************************/
5751 typedef void ma_data_source;
5752
5753 #define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001
5754
5755 typedef struct
5756 {
5757 ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
5758 ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);
5759 ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
5760 ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);
5761 ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);
5762 ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping);
5763 ma_uint32 flags;
5764 } ma_data_source_vtable;
5765
5766 typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource);
5767
5768 typedef struct
5769 {
5770 const ma_data_source_vtable* vtable;
5771 } ma_data_source_config;
5772
5773 MA_API ma_data_source_config ma_data_source_config_init(void);
5774
5775
5776 typedef struct
5777 {
5778 const ma_data_source_vtable* vtable;
5779 ma_uint64 rangeBegInFrames;
5780 ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */
5781 ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */
5782 ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */
5783 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. */
5784 ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */
5785 ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */
5786 MA_ATOMIC(4, ma_bool32) isLooping;
5787 } ma_data_source_base;
5788
5789 MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource);
5790 MA_API void ma_data_source_uninit(ma_data_source* pDataSource);
5791 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. */
5792 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); */
5793 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);
5794 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);
5795 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
5796 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. */
5797 MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor);
5798 MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength);
5799 MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping);
5800 MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource);
5801 MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames);
5802 MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
5803 MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames);
5804 MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
5805 MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource);
5806 MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource);
5807 MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource);
5808 MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource);
5809 MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext);
5810 MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource);
5811
5812
5813 typedef struct
5814 {
5815 ma_data_source_base ds;
5816 ma_format format;
5817 ma_uint32 channels;
5818 ma_uint32 sampleRate;
5819 ma_uint64 cursor;
5820 ma_uint64 sizeInFrames;
5821 const void* pData;
5822 } ma_audio_buffer_ref;
5823
5824 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);
5825 MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef);
5826 MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);
5827 MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
5828 MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex);
5829 MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);
5830 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. */
5831 MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef);
5832 MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor);
5833 MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength);
5834 MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames);
5835
5836
5837
5838 typedef struct
5839 {
5840 ma_format format;
5841 ma_uint32 channels;
5842 ma_uint32 sampleRate;
5843 ma_uint64 sizeInFrames;
5844 const void* pData; /* If set to NULL, will allocate a block of memory for you. */
5845 ma_allocation_callbacks allocationCallbacks;
5846 } ma_audio_buffer_config;
5847
5848 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);
5849
5850 typedef struct
5851 {
5852 ma_audio_buffer_ref ref;
5853 ma_allocation_callbacks allocationCallbacks;
5854 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(). */
5855 ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */
5856 } ma_audio_buffer;
5857
5858 MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
5859 MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
5860 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. */
5861 MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer);
5862 MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer);
5863 MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
5864 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex);
5865 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
5866 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. */
5867 MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer);
5868 MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor);
5869 MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength);
5870 MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);
5871
5872
5873 /*
5874 Paged Audio Buffer
5875 ==================
5876 A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It
5877 can be used for cases where audio data is streamed in asynchronously while allowing data to be read
5878 at the same time.
5879
5880 This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across
5881 simultaneously across different threads, however only one thread at a time can append, and only one
5882 thread at a time can read and seek.
5883 */
5884 typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page;
5885 struct ma_paged_audio_buffer_page
5886 {
5887 MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext;
5888 ma_uint64 sizeInFrames;
5889 ma_uint8 pAudioData[1];
5890 };
5891
5892 typedef struct
5893 {
5894 ma_format format;
5895 ma_uint32 channels;
5896 ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */
5897 MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */
5898 } ma_paged_audio_buffer_data;
5899
5900 MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData);
5901 MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks);
5902 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData);
5903 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData);
5904 MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength);
5905 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);
5906 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);
5907 MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage);
5908 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);
5909
5910
5911 typedef struct
5912 {
5913 ma_paged_audio_buffer_data* pData; /* Must not be null. */
5914 } ma_paged_audio_buffer_config;
5915
5916 MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData);
5917
5918
5919 typedef struct
5920 {
5921 ma_data_source_base ds;
5922 ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */
5923 ma_paged_audio_buffer_page* pCurrent;
5924 ma_uint64 relativeCursor; /* Relative to the current page. */
5925 ma_uint64 absoluteCursor;
5926 } ma_paged_audio_buffer;
5927
5928 MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer);
5929 MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer);
5930 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. */
5931 MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex);
5932 MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor);
5933 MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength);
5934
5935
5936
5937 /************************************************************************************************************************************************************
5938
5939 Ring Buffer
5940
5941 ************************************************************************************************************************************************************/
5942 typedef struct
5943 {
5944 void* pBuffer;
5945 ma_uint32 subbufferSizeInBytes;
5946 ma_uint32 subbufferCount;
5947 ma_uint32 subbufferStrideInBytes;
5948 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. */
5949 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. */
5950 ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
5951 ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
5952 ma_allocation_callbacks allocationCallbacks;
5953 } ma_rb;
5954
5955 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);
5956 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
5957 MA_API void ma_rb_uninit(ma_rb* pRB);
5958 MA_API void ma_rb_reset(ma_rb* pRB);
5959 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
5960 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes);
5961 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
5962 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes);
5963 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
5964 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
5965 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. */
5966 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB);
5967 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB);
5968 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB);
5969 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);
5970 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
5971 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
5972
5973
5974 typedef struct
5975 {
5976 ma_data_source_base ds;
5977 ma_rb rb;
5978 ma_format format;
5979 ma_uint32 channels;
5980 ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */
5981 } ma_pcm_rb;
5982
5983 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);
5984 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);
5985 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB);
5986 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB);
5987 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
5988 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames);
5989 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
5990 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames);
5991 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
5992 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
5993 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */
5994 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB);
5995 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB);
5996 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB);
5997 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB);
5998 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex);
5999 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
6000 MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB);
6001 MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB);
6002 MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB);
6003 MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate);
6004
6005
6006 /*
6007 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
6008 capture device writes to it, and then a playback device reads from it.
6009
6010 At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly
6011 handle desyncs. Note that the API is work in progress and may change at any time in any version.
6012
6013 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
6014 in frames. The internal sample rate of the capture device is also needed in order to calculate the size.
6015 */
6016 typedef struct
6017 {
6018 ma_pcm_rb rb;
6019 } ma_duplex_rb;
6020
6021 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);
6022 MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB);
6023
6024
6025 /************************************************************************************************************************************************************
6026
6027 Miscellaneous Helpers
6028
6029 ************************************************************************************************************************************************************/
6030 /*
6031 Retrieves a human readable description of the given result code.
6032 */
6033 MA_API const char* ma_result_description(ma_result result);
6034
6035 /*
6036 malloc()
6037 */
6038 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
6039
6040 /*
6041 calloc()
6042 */
6043 MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
6044
6045 /*
6046 realloc()
6047 */
6048 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
6049
6050 /*
6051 free()
6052 */
6053 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
6054
6055 /*
6056 Performs an aligned malloc, with the assumption that the alignment is a power of 2.
6057 */
6058 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
6059
6060 /*
6061 Free's an aligned malloc'd buffer.
6062 */
6063 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
6064
6065 /*
6066 Retrieves a friendly name for a format.
6067 */
6068 MA_API const char* ma_get_format_name(ma_format format);
6069
6070 /*
6071 Blends two frames in floating point format.
6072 */
6073 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
6074
6075 /*
6076 Retrieves the size of a sample in bytes for the given format.
6077
6078 This API is efficient and is implemented using a lookup table.
6079
6080 Thread Safety: SAFE
6081 This API is pure.
6082 */
6083 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format);
6084 static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
6085
6086 /*
6087 Converts a log level to a string.
6088 */
6089 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel);
6090
6091
6092
6093
6094 /************************************************************************************************************************************************************
6095
6096 Synchronization
6097
6098 ************************************************************************************************************************************************************/
6099 /*
6100 Locks a spinlock.
6101 */
6102 MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock);
6103
6104 /*
6105 Locks a spinlock, but does not yield() when looping.
6106 */
6107 MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock);
6108
6109 /*
6110 Unlocks a spinlock.
6111 */
6112 MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock);
6113
6114
6115 #ifndef MA_NO_THREADING
6116
6117 /*
6118 Creates a mutex.
6119
6120 A mutex must be created from a valid context. A mutex is initially unlocked.
6121 */
6122 MA_API ma_result ma_mutex_init(ma_mutex* pMutex);
6123
6124 /*
6125 Deletes a mutex.
6126 */
6127 MA_API void ma_mutex_uninit(ma_mutex* pMutex);
6128
6129 /*
6130 Locks a mutex with an infinite timeout.
6131 */
6132 MA_API void ma_mutex_lock(ma_mutex* pMutex);
6133
6134 /*
6135 Unlocks a mutex.
6136 */
6137 MA_API void ma_mutex_unlock(ma_mutex* pMutex);
6138
6139
6140 /*
6141 Initializes an auto-reset event.
6142 */
6143 MA_API ma_result ma_event_init(ma_event* pEvent);
6144
6145 /*
6146 Uninitializes an auto-reset event.
6147 */
6148 MA_API void ma_event_uninit(ma_event* pEvent);
6149
6150 /*
6151 Waits for the specified auto-reset event to become signalled.
6152 */
6153 MA_API ma_result ma_event_wait(ma_event* pEvent);
6154
6155 /*
6156 Signals the specified auto-reset event.
6157 */
6158 MA_API ma_result ma_event_signal(ma_event* pEvent);
6159 #endif /* MA_NO_THREADING */
6160
6161
6162 /*
6163 Fence
6164 =====
6165 This locks while the counter is larger than 0. Counter can be incremented and decremented by any
6166 thread, but care needs to be taken when waiting. It is possible for one thread to acquire the
6167 fence just as another thread returns from ma_fence_wait().
6168
6169 The idea behind a fence is to allow you to wait for a group of operations to complete. When an
6170 operation starts, the counter is incremented which locks the fence. When the operation completes,
6171 the fence will be released which decrements the counter. ma_fence_wait() will block until the
6172 counter hits zero.
6173
6174 If threading is disabled, ma_fence_wait() will spin on the counter.
6175 */
6176 typedef struct
6177 {
6178 #ifndef MA_NO_THREADING
6179 ma_event e;
6180 #endif
6181 ma_uint32 counter;
6182 } ma_fence;
6183
6184 MA_API ma_result ma_fence_init(ma_fence* pFence);
6185 MA_API void ma_fence_uninit(ma_fence* pFence);
6186 MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */
6187 MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */
6188 MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */
6189
6190
6191
6192 /*
6193 Notification callback for asynchronous operations.
6194 */
6195 typedef void ma_async_notification;
6196
6197 typedef struct
6198 {
6199 void (* onSignal)(ma_async_notification* pNotification);
6200 } ma_async_notification_callbacks;
6201
6202 MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification);
6203
6204
6205 /*
6206 Simple polling notification.
6207
6208 This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled()
6209 */
6210 typedef struct
6211 {
6212 ma_async_notification_callbacks cb;
6213 ma_bool32 signalled;
6214 } ma_async_notification_poll;
6215
6216 MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll);
6217 MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll);
6218
6219
6220 /*
6221 Event Notification
6222
6223 This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail.
6224 */
6225 typedef struct
6226 {
6227 ma_async_notification_callbacks cb;
6228 #ifndef MA_NO_THREADING
6229 ma_event e;
6230 #endif
6231 } ma_async_notification_event;
6232
6233 MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent);
6234 MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent);
6235 MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent);
6236 MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent);
6237
6238
6239
6240
6241 /************************************************************************************************************************************************************
6242
6243 Job Queue
6244
6245 ************************************************************************************************************************************************************/
6246
6247 /*
6248 Slot Allocator
6249 --------------
6250 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
6251 as the insertion point for an object.
6252
6253 Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
6254
6255 The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits:
6256
6257 +-----------------+-----------------+
6258 | 32 Bits | 32 Bits |
6259 +-----------------+-----------------+
6260 | Reference Count | Slot Index |
6261 +-----------------+-----------------+
6262 */
6263 typedef struct
6264 {
6265 ma_uint32 capacity; /* The number of slots to make available. */
6266 } ma_slot_allocator_config;
6267
6268 MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity);
6269
6270
6271 typedef struct
6272 {
6273 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. */
6274 } ma_slot_allocator_group;
6275
6276 typedef struct
6277 {
6278 ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */
6279 ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */
6280 ma_uint32 count; /* Allocation count. */
6281 ma_uint32 capacity;
6282
6283 /* Memory management. */
6284 ma_bool32 _ownsHeap;
6285 void* _pHeap;
6286 } ma_slot_allocator;
6287
6288 MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes);
6289 MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator);
6290 MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator);
6291 MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks);
6292 MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot);
6293 MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot);
6294
6295
6296 typedef struct ma_job ma_job;
6297
6298 /*
6299 Callback for processing a job. Each job type will have their own processing callback which will be
6300 called by ma_job_process().
6301 */
6302 typedef ma_result (* ma_job_proc)(ma_job* pJob);
6303
6304 /* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */
6305 typedef enum
6306 {
6307 /* Miscellaneous. */
6308 MA_JOB_TYPE_QUIT = 0,
6309 MA_JOB_TYPE_CUSTOM,
6310
6311 /* Resource Manager. */
6312 MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE,
6313 MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE,
6314 MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE,
6315 MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER,
6316 MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER,
6317 MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM,
6318 MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM,
6319 MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM,
6320 MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM,
6321
6322 /* Device. */
6323 MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE,
6324
6325 /* Count. Must always be last. */
6326 MA_JOB_TYPE_COUNT
6327 } ma_job_type;
6328
6329 struct ma_job
6330 {
6331 union
6332 {
6333 struct
6334 {
6335 ma_uint16 code; /* Job type. */
6336 ma_uint16 slot; /* Index into a ma_slot_allocator. */
6337 ma_uint32 refcount;
6338 } breakup;
6339 ma_uint64 allocation;
6340 } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */
6341 MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */
6342 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. */
6343
6344 union
6345 {
6346 /* Miscellaneous. */
6347 struct
6348 {
6349 ma_job_proc proc;
6350 ma_uintptr data0;
6351 ma_uintptr data1;
6352 } custom;
6353
6354 /* Resource Manager */
6355 union
6356 {
6357 struct
6358 {
6359 /*ma_resource_manager**/ void* pResourceManager;
6360 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
6361 char* pFilePath;
6362 wchar_t* pFilePathW;
6363 ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */
6364 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
6365 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. */
6366 ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */
6367 ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */
6368 } loadDataBufferNode;
6369 struct
6370 {
6371 /*ma_resource_manager**/ void* pResourceManager;
6372 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
6373 ma_async_notification* pDoneNotification;
6374 ma_fence* pDoneFence;
6375 } freeDataBufferNode;
6376 struct
6377 {
6378 /*ma_resource_manager**/ void* pResourceManager;
6379 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
6380 /*ma_decoder**/ void* pDecoder;
6381 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
6382 ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */
6383 } pageDataBufferNode;
6384
6385 struct
6386 {
6387 /*ma_resource_manager_data_buffer**/ void* pDataBuffer;
6388 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
6389 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
6390 ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
6391 ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */
6392 ma_uint64 rangeBegInPCMFrames;
6393 ma_uint64 rangeEndInPCMFrames;
6394 ma_uint64 loopPointBegInPCMFrames;
6395 ma_uint64 loopPointEndInPCMFrames;
6396 ma_uint32 isLooping;
6397 } loadDataBuffer;
6398 struct
6399 {
6400 /*ma_resource_manager_data_buffer**/ void* pDataBuffer;
6401 ma_async_notification* pDoneNotification;
6402 ma_fence* pDoneFence;
6403 } freeDataBuffer;
6404
6405 struct
6406 {
6407 /*ma_resource_manager_data_stream**/ void* pDataStream;
6408 char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */
6409 wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */
6410 ma_uint64 initialSeekPoint;
6411 ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */
6412 ma_fence* pInitFence;
6413 } loadDataStream;
6414 struct
6415 {
6416 /*ma_resource_manager_data_stream**/ void* pDataStream;
6417 ma_async_notification* pDoneNotification;
6418 ma_fence* pDoneFence;
6419 } freeDataStream;
6420 struct
6421 {
6422 /*ma_resource_manager_data_stream**/ void* pDataStream;
6423 ma_uint32 pageIndex; /* The index of the page to decode into. */
6424 } pageDataStream;
6425 struct
6426 {
6427 /*ma_resource_manager_data_stream**/ void* pDataStream;
6428 ma_uint64 frameIndex;
6429 } seekDataStream;
6430 } resourceManager;
6431
6432 /* Device. */
6433 union
6434 {
6435 union
6436 {
6437 struct
6438 {
6439 /*ma_device**/ void* pDevice;
6440 /*ma_device_type*/ ma_uint32 deviceType;
6441 } reroute;
6442 } aaudio;
6443 } device;
6444 } data;
6445 };
6446
6447 MA_API ma_job ma_job_init(ma_uint16 code);
6448 MA_API ma_result ma_job_process(ma_job* pJob);
6449
6450
6451 /*
6452 When set, ma_job_queue_next() will not wait and no semaphore will be signaled in
6453 ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available.
6454
6455 This flag should always be used for platforms that do not support multithreading.
6456 */
6457 typedef enum
6458 {
6459 MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001
6460 } ma_job_queue_flags;
6461
6462 typedef struct
6463 {
6464 ma_uint32 flags;
6465 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */
6466 } ma_job_queue_config;
6467
6468 MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity);
6469
6470
6471 typedef struct
6472 {
6473 ma_uint32 flags; /* Flags passed in at initialization time. */
6474 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */
6475 MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */
6476 MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */
6477 #ifndef MA_NO_THREADING
6478 ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */
6479 #endif
6480 ma_slot_allocator allocator;
6481 ma_job* pJobs;
6482 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
6483 ma_spinlock lock;
6484 #endif
6485
6486 /* Memory management. */
6487 void* _pHeap;
6488 ma_bool32 _ownsHeap;
6489 } ma_job_queue;
6490
6491 MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes);
6492 MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue);
6493 MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue);
6494 MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks);
6495 MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob);
6496 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. */
6497
6498
6499
6500 /************************************************************************************************************************************************************
6501 *************************************************************************************************************************************************************
6502
6503 DEVICE I/O
6504 ==========
6505
6506 This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
6507
6508 *************************************************************************************************************************************************************
6509 ************************************************************************************************************************************************************/
6510 #ifndef MA_NO_DEVICE_IO
6511 /* Some backends are only supported on certain platforms. */
6512 #if defined(MA_WIN32)
6513 #define MA_SUPPORT_WASAPI
6514
6515 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
6516 #define MA_SUPPORT_DSOUND
6517 #define MA_SUPPORT_WINMM
6518
6519 /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */
6520 #if !defined(__COSMOPOLITAN__)
6521 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
6522 #endif
6523 #endif
6524 #endif
6525 #if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO)
6526 #if defined(MA_LINUX)
6527 #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */
6528 #define MA_SUPPORT_ALSA
6529 #endif
6530 #endif
6531 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
6532 #define MA_SUPPORT_PULSEAUDIO
6533 #define MA_SUPPORT_JACK
6534 #endif
6535 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
6536 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
6537 #endif
6538 #if defined(__NetBSD__) || defined(__OpenBSD__)
6539 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
6540 #endif
6541 #if defined(__FreeBSD__) || defined(__DragonFly__)
6542 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
6543 #endif
6544 #endif
6545 #if defined(MA_ANDROID)
6546 #define MA_SUPPORT_AAUDIO
6547 #define MA_SUPPORT_OPENSL
6548 #endif
6549 #if defined(MA_APPLE)
6550 #define MA_SUPPORT_COREAUDIO
6551 #endif
6552 #if defined(MA_EMSCRIPTEN)
6553 #define MA_SUPPORT_WEBAUDIO
6554 #endif
6555
6556 /* All platforms should support custom backends. */
6557 #define MA_SUPPORT_CUSTOM
6558
6559 /* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
6560 #if !defined(MA_EMSCRIPTEN)
6561 #define MA_SUPPORT_NULL
6562 #endif
6563
6564
6565 #if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
6566 #define MA_HAS_WASAPI
6567 #endif
6568 #if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
6569 #define MA_HAS_DSOUND
6570 #endif
6571 #if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
6572 #define MA_HAS_WINMM
6573 #endif
6574 #if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
6575 #define MA_HAS_ALSA
6576 #endif
6577 #if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
6578 #define MA_HAS_PULSEAUDIO
6579 #endif
6580 #if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
6581 #define MA_HAS_JACK
6582 #endif
6583 #if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
6584 #define MA_HAS_COREAUDIO
6585 #endif
6586 #if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
6587 #define MA_HAS_SNDIO
6588 #endif
6589 #if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
6590 #define MA_HAS_AUDIO4
6591 #endif
6592 #if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
6593 #define MA_HAS_OSS
6594 #endif
6595 #if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
6596 #define MA_HAS_AAUDIO
6597 #endif
6598 #if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
6599 #define MA_HAS_OPENSL
6600 #endif
6601 #if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
6602 #define MA_HAS_WEBAUDIO
6603 #endif
6604 #if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
6605 #define MA_HAS_CUSTOM
6606 #endif
6607 #if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
6608 #define MA_HAS_NULL
6609 #endif
6610
6611 typedef enum
6612 {
6613 ma_device_state_uninitialized = 0,
6614 ma_device_state_stopped = 1, /* The device's default state after initialization. */
6615 ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */
6616 ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */
6617 ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */
6618 } ma_device_state;
6619
6620 MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state)
6621
6622
6623 #ifdef MA_SUPPORT_WASAPI
6624 /* We need a IMMNotificationClient object for WASAPI. */
6625 typedef struct
6626 {
6627 void* lpVtbl;
6628 ma_uint32 counter;
6629 ma_device* pDevice;
6630 } ma_IMMNotificationClient;
6631 #endif
6632
6633 /* Backend enums must be in priority order. */
6634 typedef enum
6635 {
6636 ma_backend_wasapi,
6637 ma_backend_dsound,
6638 ma_backend_winmm,
6639 ma_backend_coreaudio,
6640 ma_backend_sndio,
6641 ma_backend_audio4,
6642 ma_backend_oss,
6643 ma_backend_pulseaudio,
6644 ma_backend_alsa,
6645 ma_backend_jack,
6646 ma_backend_aaudio,
6647 ma_backend_opensl,
6648 ma_backend_webaudio,
6649 ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
6650 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
6651 } ma_backend;
6652
6653 #define MA_BACKEND_COUNT (ma_backend_null+1)
6654
6655
6656 /*
6657 Device job thread. This is used by backends that require asynchronous processing of certain
6658 operations. It is not used by all backends.
6659
6660 The device job thread is made up of a thread and a job queue. You can post a job to the thread with
6661 ma_device_job_thread_post(). The thread will do the processing of the job.
6662 */
6663 typedef struct
6664 {
6665 ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */
6666 ma_uint32 jobQueueCapacity;
6667 ma_uint32 jobQueueFlags;
6668 } ma_device_job_thread_config;
6669
6670 MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void);
6671
6672 typedef struct
6673 {
6674 ma_thread thread;
6675 ma_job_queue jobQueue;
6676 ma_bool32 _hasThread;
6677 } ma_device_job_thread;
6678
6679 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);
6680 MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks);
6681 MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob);
6682 MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob);
6683
6684
6685
6686 /* Device notification types. */
6687 typedef enum
6688 {
6689 ma_device_notification_type_started,
6690 ma_device_notification_type_stopped,
6691 ma_device_notification_type_rerouted,
6692 ma_device_notification_type_interruption_began,
6693 ma_device_notification_type_interruption_ended
6694 } ma_device_notification_type;
6695
6696 typedef struct
6697 {
6698 ma_device* pDevice;
6699 ma_device_notification_type type;
6700 union
6701 {
6702 struct
6703 {
6704 int _unused;
6705 } started;
6706 struct
6707 {
6708 int _unused;
6709 } stopped;
6710 struct
6711 {
6712 int _unused;
6713 } rerouted;
6714 struct
6715 {
6716 int _unused;
6717 } interruption;
6718 } data;
6719 } ma_device_notification;
6720
6721 /*
6722 The notification callback for when the application should be notified of a change to the device.
6723
6724 This callback is used for notifying the application of changes such as when the device has started,
6725 stopped, rerouted or an interruption has occurred. Note that not all backends will post all
6726 notification types. For example, some backends will perform automatic stream routing without any
6727 kind of notification to the host program which means miniaudio will never know about it and will
6728 never be able to fire the rerouted notification. You should keep this in mind when designing your
6729 program.
6730
6731 The stopped notification will *not* get fired when a device is rerouted.
6732
6733
6734 Parameters
6735 ----------
6736 pNotification (in)
6737 A pointer to a structure containing information about the event. Use the `pDevice` member of
6738 this object to retrieve the relevant device. The `type` member can be used to discriminate
6739 against each of the notification types.
6740
6741
6742 Remarks
6743 -------
6744 Do not restart or uninitialize the device from the callback.
6745
6746 Not all notifications will be triggered by all backends, however the started and stopped events
6747 should be reliable for all backends. Some backends do not have a good way to detect device
6748 stoppages due to unplugging the device which may result in the stopped callback not getting
6749 fired. This has been observed with at least one BSD variant.
6750
6751 The rerouted notification is fired *after* the reroute has occurred. The stopped notification will
6752 *not* get fired when a device is rerouted. The following backends are known to do automatic stream
6753 rerouting, but do not have a way to be notified of the change:
6754
6755 * DirectSound
6756
6757 The interruption notifications are used on mobile platforms for detecting when audio is interrupted
6758 due to things like an incoming phone call. Currently this is only implemented on iOS. None of the
6759 Android backends will report this notification.
6760 */
6761 typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification);
6762
6763
6764 /*
6765 The callback for processing audio data from the device.
6766
6767 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
6768 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
6769 callback will be fired with a consistent frame count.
6770
6771
6772 Parameters
6773 ----------
6774 pDevice (in)
6775 A pointer to the relevant device.
6776
6777 pOutput (out)
6778 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
6779 full-duplex device and null for a capture and loopback device.
6780
6781 pInput (in)
6782 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
6783 playback device.
6784
6785 frameCount (in)
6786 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
6787 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
6788 not assume this will always be the same value each time the callback is fired.
6789
6790
6791 Remarks
6792 -------
6793 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
6794 callback. The following APIs cannot be called from inside the callback:
6795
6796 ma_device_init()
6797 ma_device_init_ex()
6798 ma_device_uninit()
6799 ma_device_start()
6800 ma_device_stop()
6801
6802 The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
6803 */
6804 typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
6805
6806
6807
6808
6809 /*
6810 DEPRECATED. Use ma_device_notification_proc instead.
6811
6812 The callback for when the device has been stopped.
6813
6814 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
6815 such as being unplugged or an internal error occurring.
6816
6817
6818 Parameters
6819 ----------
6820 pDevice (in)
6821 A pointer to the device that has just stopped.
6822
6823
6824 Remarks
6825 -------
6826 Do not restart or uninitialize the device from the callback.
6827 */
6828 typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */
6829
6830 typedef enum
6831 {
6832 ma_device_type_playback = 1,
6833 ma_device_type_capture = 2,
6834 ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */
6835 ma_device_type_loopback = 4
6836 } ma_device_type;
6837
6838 typedef enum
6839 {
6840 ma_share_mode_shared = 0,
6841 ma_share_mode_exclusive
6842 } ma_share_mode;
6843
6844 /* iOS/tvOS/watchOS session categories. */
6845 typedef enum
6846 {
6847 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */
6848 ma_ios_session_category_none, /* Leave the session category unchanged. */
6849 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
6850 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
6851 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
6852 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
6853 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
6854 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
6855 } ma_ios_session_category;
6856
6857 /* iOS/tvOS/watchOS session category options */
6858 typedef enum
6859 {
6860 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
6861 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
6862 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
6863 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
6864 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
6865 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
6866 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
6867 } ma_ios_session_category_option;
6868
6869 /* OpenSL stream types. */
6870 typedef enum
6871 {
6872 ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */
6873 ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */
6874 ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */
6875 ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */
6876 ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */
6877 ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */
6878 ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */
6879 } ma_opensl_stream_type;
6880
6881 /* OpenSL recording presets. */
6882 typedef enum
6883 {
6884 ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */
6885 ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */
6886 ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */
6887 ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */
6888 ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */
6889 ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
6890 } ma_opensl_recording_preset;
6891
6892 /* WASAPI audio thread priority characteristics. */
6893 typedef enum
6894 {
6895 ma_wasapi_usage_default = 0,
6896 ma_wasapi_usage_games,
6897 ma_wasapi_usage_pro_audio,
6898 } ma_wasapi_usage;
6899
6900 /* AAudio usage types. */
6901 typedef enum
6902 {
6903 ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */
6904 ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */
6905 ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */
6906 ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
6907 ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */
6908 ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */
6909 ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
6910 ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */
6911 ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
6912 ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
6913 ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
6914 ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */
6915 ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */
6916 ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
6917 ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */
6918 ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
6919 ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
6920 } ma_aaudio_usage;
6921
6922 /* AAudio content types. */
6923 typedef enum
6924 {
6925 ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */
6926 ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */
6927 ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */
6928 ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */
6929 ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */
6930 } ma_aaudio_content_type;
6931
6932 /* AAudio input presets. */
6933 typedef enum
6934 {
6935 ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */
6936 ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */
6937 ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */
6938 ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
6939 ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
6940 ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */
6941 ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
6942 } ma_aaudio_input_preset;
6943
6944 typedef enum
6945 {
6946 ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */
6947 ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */
6948 ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */
6949 ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */
6950 } ma_aaudio_allowed_capture_policy;
6951
6952 typedef union
6953 {
6954 ma_int64 counter;
6955 double counterD;
6956 } ma_timer;
6957
6958 typedef union
6959 {
6960 ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
6961 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
6962 /*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. */
6963 char alsa[256]; /* ALSA uses a name string for identification. */
6964 char pulse[256]; /* PulseAudio uses a name string for identification. */
6965 int jack; /* JACK always uses default devices. */
6966 char coreaudio[256]; /* Core Audio uses a string for identification. */
6967 char sndio[256]; /* "snd/0", etc. */
6968 char audio4[256]; /* "/dev/audio", etc. */
6969 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
6970 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
6971 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
6972 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
6973 union
6974 {
6975 int i;
6976 char s[256];
6977 void* p;
6978 } custom; /* The custom backend could be anything. Give them a few options. */
6979 int nullbackend; /* The null backend uses an integer for device IDs. */
6980 } ma_device_id;
6981
6982
6983 typedef struct ma_context_config ma_context_config;
6984 typedef struct ma_device_config ma_device_config;
6985 typedef struct ma_backend_callbacks ma_backend_callbacks;
6986
6987 #define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */
6988
6989 #ifndef MA_MAX_DEVICE_NAME_LENGTH
6990 #define MA_MAX_DEVICE_NAME_LENGTH 255
6991 #endif
6992
6993 typedef struct
6994 {
6995 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
6996 ma_device_id id;
6997 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */
6998 ma_bool32 isDefault;
6999
7000 ma_uint32 nativeDataFormatCount;
7001 struct
7002 {
7003 ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */
7004 ma_uint32 channels; /* If set to 0, all channels are supported. */
7005 ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */
7006 ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */
7007 } 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. */
7008 } ma_device_info;
7009
7010 struct ma_device_config
7011 {
7012 ma_device_type deviceType;
7013 ma_uint32 sampleRate;
7014 ma_uint32 periodSizeInFrames;
7015 ma_uint32 periodSizeInMilliseconds;
7016 ma_uint32 periods;
7017 ma_performance_profile performanceProfile;
7018 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. */
7019 ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */
7020 ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */
7021 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. */
7022 ma_device_data_proc dataCallback;
7023 ma_device_notification_proc notificationCallback;
7024 ma_stop_proc stopCallback;
7025 void* pUserData;
7026 ma_resampler_config resampling;
7027 struct
7028 {
7029 const ma_device_id* pDeviceID;
7030 ma_format format;
7031 ma_uint32 channels;
7032 ma_channel* pChannelMap;
7033 ma_channel_mix_mode channelMixMode;
7034 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. */
7035 ma_share_mode shareMode;
7036 } playback;
7037 struct
7038 {
7039 const ma_device_id* pDeviceID;
7040 ma_format format;
7041 ma_uint32 channels;
7042 ma_channel* pChannelMap;
7043 ma_channel_mix_mode channelMixMode;
7044 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. */
7045 ma_share_mode shareMode;
7046 } capture;
7047
7048 struct
7049 {
7050 ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */
7051 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
7052 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
7053 ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */
7054 ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
7055 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. */
7056 ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */
7057 } wasapi;
7058 struct
7059 {
7060 ma_bool32 noMMap; /* Disables MMap mode. */
7061 ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */
7062 ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */
7063 ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */
7064 } alsa;
7065 struct
7066 {
7067 const char* pStreamNamePlayback;
7068 const char* pStreamNameCapture;
7069 } pulse;
7070 struct
7071 {
7072 ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
7073 } coreaudio;
7074 struct
7075 {
7076 ma_opensl_stream_type streamType;
7077 ma_opensl_recording_preset recordingPreset;
7078 ma_bool32 enableCompatibilityWorkarounds;
7079 } opensl;
7080 struct
7081 {
7082 ma_aaudio_usage usage;
7083 ma_aaudio_content_type contentType;
7084 ma_aaudio_input_preset inputPreset;
7085 ma_aaudio_allowed_capture_policy allowedCapturePolicy;
7086 ma_bool32 noAutoStartAfterReroute;
7087 ma_bool32 enableCompatibilityWorkarounds;
7088 } aaudio;
7089 };
7090
7091
7092 /*
7093 The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`.
7094
7095
7096 Parameters
7097 ----------
7098 pContext (in)
7099 A pointer to the context performing the enumeration.
7100
7101 deviceType (in)
7102 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.
7103
7104 pInfo (in)
7105 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,
7106 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
7107 is too inefficient.
7108
7109 pUserData (in)
7110 The user data pointer passed into `ma_context_enumerate_devices()`.
7111 */
7112 typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
7113
7114
7115 /*
7116 Describes some basic details about a playback or capture device.
7117 */
7118 typedef struct
7119 {
7120 const ma_device_id* pDeviceID;
7121 ma_share_mode shareMode;
7122 ma_format format;
7123 ma_uint32 channels;
7124 ma_uint32 sampleRate;
7125 ma_channel channelMap[MA_MAX_CHANNELS];
7126 ma_uint32 periodSizeInFrames;
7127 ma_uint32 periodSizeInMilliseconds;
7128 ma_uint32 periodCount;
7129 } ma_device_descriptor;
7130
7131 /*
7132 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
7133 to many devices. A device is created from a context.
7134
7135 The general flow goes like this:
7136
7137 1) A context is created with `onContextInit()`
7138 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
7139 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
7140 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was
7141 selected from device enumeration via `onContextEnumerateDevices()`.
7142 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`
7143 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
7144 to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
7145 miniaudio internally.
7146
7147 Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the
7148 callbacks defined in this structure.
7149
7150 Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which
7151 physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the
7152 given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration
7153 needs to stop and the `onContextEnumerateDevices()` function returns with a success code.
7154
7155 Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,
7156 and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the
7157 case when the device ID is NULL, in which case information about the default device needs to be retrieved.
7158
7159 Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
7160 This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a
7161 device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
7162 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
7163 the requested format. The conversion between the format requested by the application and the device's native format will be handled
7164 internally by miniaudio.
7165
7166 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
7167 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
7168 sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to
7169 `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
7170 inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period
7171 size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
7172 sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor`
7173 object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
7174
7175 Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
7176 asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented.
7177
7178 The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
7179 easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
7180 `onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
7181 backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
7182 This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
7183
7184 If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
7185 which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
7186
7187 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
7188 encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.
7189
7190 The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this
7191 callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated
7192 which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,
7193 look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to
7194 wake up the audio thread.
7195
7196 If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the
7197 `onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient.
7198 */
7199 struct ma_backend_callbacks
7200 {
7201 ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);
7202 ma_result (* onContextUninit)(ma_context* pContext);
7203 ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
7204 ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
7205 ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);
7206 ma_result (* onDeviceUninit)(ma_device* pDevice);
7207 ma_result (* onDeviceStart)(ma_device* pDevice);
7208 ma_result (* onDeviceStop)(ma_device* pDevice);
7209 ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);
7210 ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);
7211 ma_result (* onDeviceDataLoop)(ma_device* pDevice);
7212 ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice);
7213 ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo);
7214 };
7215
7216 struct ma_context_config
7217 {
7218 ma_log* pLog;
7219 ma_thread_priority threadPriority;
7220 size_t threadStackSize;
7221 void* pUserData;
7222 ma_allocation_callbacks allocationCallbacks;
7223 struct
7224 {
7225 ma_bool32 useVerboseDeviceEnumeration;
7226 } alsa;
7227 struct
7228 {
7229 const char* pApplicationName;
7230 const char* pServerName;
7231 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
7232 } pulse;
7233 struct
7234 {
7235 ma_ios_session_category sessionCategory;
7236 ma_uint32 sessionCategoryOptions;
7237 ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */
7238 ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */
7239 } coreaudio;
7240 struct
7241 {
7242 const char* pClientName;
7243 ma_bool32 tryStartServer;
7244 } jack;
7245 ma_backend_callbacks custom;
7246 };
7247
7248 /* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */
7249 typedef struct
7250 {
7251 int code;
7252 ma_event* pEvent; /* This will be signalled when the event is complete. */
7253 union
7254 {
7255 struct
7256 {
7257 int _unused;
7258 } quit;
7259 struct
7260 {
7261 ma_device_type deviceType;
7262 void* pAudioClient;
7263 void** ppAudioClientService;
7264 ma_result* pResult; /* The result from creating the audio client service. */
7265 } createAudioClient;
7266 struct
7267 {
7268 ma_device* pDevice;
7269 ma_device_type deviceType;
7270 } releaseAudioClient;
7271 } data;
7272 } ma_context_command__wasapi;
7273
7274 struct ma_context
7275 {
7276 ma_backend_callbacks callbacks;
7277 ma_backend backend; /* DirectSound, ALSA, etc. */
7278 ma_log* pLog;
7279 ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */
7280 ma_thread_priority threadPriority;
7281 size_t threadStackSize;
7282 void* pUserData;
7283 ma_allocation_callbacks allocationCallbacks;
7284 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
7285 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
7286 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
7287 ma_uint32 playbackDeviceInfoCount;
7288 ma_uint32 captureDeviceInfoCount;
7289 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
7290
7291 union
7292 {
7293 #ifdef MA_SUPPORT_WASAPI
7294 struct
7295 {
7296 ma_thread commandThread;
7297 ma_mutex commandLock;
7298 ma_semaphore commandSem;
7299 ma_uint32 commandIndex;
7300 ma_uint32 commandCount;
7301 ma_context_command__wasapi commands[4];
7302 ma_handle hAvrt;
7303 ma_proc AvSetMmThreadCharacteristicsA;
7304 ma_proc AvRevertMmThreadcharacteristics;
7305 ma_handle hMMDevapi;
7306 ma_proc ActivateAudioInterfaceAsync;
7307 } wasapi;
7308 #endif
7309 #ifdef MA_SUPPORT_DSOUND
7310 struct
7311 {
7312 ma_handle hDSoundDLL;
7313 ma_proc DirectSoundCreate;
7314 ma_proc DirectSoundEnumerateA;
7315 ma_proc DirectSoundCaptureCreate;
7316 ma_proc DirectSoundCaptureEnumerateA;
7317 } dsound;
7318 #endif
7319 #ifdef MA_SUPPORT_WINMM
7320 struct
7321 {
7322 ma_handle hWinMM;
7323 ma_proc waveOutGetNumDevs;
7324 ma_proc waveOutGetDevCapsA;
7325 ma_proc waveOutOpen;
7326 ma_proc waveOutClose;
7327 ma_proc waveOutPrepareHeader;
7328 ma_proc waveOutUnprepareHeader;
7329 ma_proc waveOutWrite;
7330 ma_proc waveOutReset;
7331 ma_proc waveInGetNumDevs;
7332 ma_proc waveInGetDevCapsA;
7333 ma_proc waveInOpen;
7334 ma_proc waveInClose;
7335 ma_proc waveInPrepareHeader;
7336 ma_proc waveInUnprepareHeader;
7337 ma_proc waveInAddBuffer;
7338 ma_proc waveInStart;
7339 ma_proc waveInReset;
7340 } winmm;
7341 #endif
7342 #ifdef MA_SUPPORT_ALSA
7343 struct
7344 {
7345 ma_handle asoundSO;
7346 ma_proc snd_pcm_open;
7347 ma_proc snd_pcm_close;
7348 ma_proc snd_pcm_hw_params_sizeof;
7349 ma_proc snd_pcm_hw_params_any;
7350 ma_proc snd_pcm_hw_params_set_format;
7351 ma_proc snd_pcm_hw_params_set_format_first;
7352 ma_proc snd_pcm_hw_params_get_format_mask;
7353 ma_proc snd_pcm_hw_params_set_channels;
7354 ma_proc snd_pcm_hw_params_set_channels_near;
7355 ma_proc snd_pcm_hw_params_set_channels_minmax;
7356 ma_proc snd_pcm_hw_params_set_rate_resample;
7357 ma_proc snd_pcm_hw_params_set_rate;
7358 ma_proc snd_pcm_hw_params_set_rate_near;
7359 ma_proc snd_pcm_hw_params_set_buffer_size_near;
7360 ma_proc snd_pcm_hw_params_set_periods_near;
7361 ma_proc snd_pcm_hw_params_set_access;
7362 ma_proc snd_pcm_hw_params_get_format;
7363 ma_proc snd_pcm_hw_params_get_channels;
7364 ma_proc snd_pcm_hw_params_get_channels_min;
7365 ma_proc snd_pcm_hw_params_get_channels_max;
7366 ma_proc snd_pcm_hw_params_get_rate;
7367 ma_proc snd_pcm_hw_params_get_rate_min;
7368 ma_proc snd_pcm_hw_params_get_rate_max;
7369 ma_proc snd_pcm_hw_params_get_buffer_size;
7370 ma_proc snd_pcm_hw_params_get_periods;
7371 ma_proc snd_pcm_hw_params_get_access;
7372 ma_proc snd_pcm_hw_params_test_format;
7373 ma_proc snd_pcm_hw_params_test_channels;
7374 ma_proc snd_pcm_hw_params_test_rate;
7375 ma_proc snd_pcm_hw_params;
7376 ma_proc snd_pcm_sw_params_sizeof;
7377 ma_proc snd_pcm_sw_params_current;
7378 ma_proc snd_pcm_sw_params_get_boundary;
7379 ma_proc snd_pcm_sw_params_set_avail_min;
7380 ma_proc snd_pcm_sw_params_set_start_threshold;
7381 ma_proc snd_pcm_sw_params_set_stop_threshold;
7382 ma_proc snd_pcm_sw_params;
7383 ma_proc snd_pcm_format_mask_sizeof;
7384 ma_proc snd_pcm_format_mask_test;
7385 ma_proc snd_pcm_get_chmap;
7386 ma_proc snd_pcm_state;
7387 ma_proc snd_pcm_prepare;
7388 ma_proc snd_pcm_start;
7389 ma_proc snd_pcm_drop;
7390 ma_proc snd_pcm_drain;
7391 ma_proc snd_pcm_reset;
7392 ma_proc snd_device_name_hint;
7393 ma_proc snd_device_name_get_hint;
7394 ma_proc snd_card_get_index;
7395 ma_proc snd_device_name_free_hint;
7396 ma_proc snd_pcm_mmap_begin;
7397 ma_proc snd_pcm_mmap_commit;
7398 ma_proc snd_pcm_recover;
7399 ma_proc snd_pcm_readi;
7400 ma_proc snd_pcm_writei;
7401 ma_proc snd_pcm_avail;
7402 ma_proc snd_pcm_avail_update;
7403 ma_proc snd_pcm_wait;
7404 ma_proc snd_pcm_nonblock;
7405 ma_proc snd_pcm_info;
7406 ma_proc snd_pcm_info_sizeof;
7407 ma_proc snd_pcm_info_get_name;
7408 ma_proc snd_pcm_poll_descriptors;
7409 ma_proc snd_pcm_poll_descriptors_count;
7410 ma_proc snd_pcm_poll_descriptors_revents;
7411 ma_proc snd_config_update_free_global;
7412
7413 ma_mutex internalDeviceEnumLock;
7414 ma_bool32 useVerboseDeviceEnumeration;
7415 } alsa;
7416 #endif
7417 #ifdef MA_SUPPORT_PULSEAUDIO
7418 struct
7419 {
7420 ma_handle pulseSO;
7421 ma_proc pa_mainloop_new;
7422 ma_proc pa_mainloop_free;
7423 ma_proc pa_mainloop_quit;
7424 ma_proc pa_mainloop_get_api;
7425 ma_proc pa_mainloop_iterate;
7426 ma_proc pa_mainloop_wakeup;
7427 ma_proc pa_threaded_mainloop_new;
7428 ma_proc pa_threaded_mainloop_free;
7429 ma_proc pa_threaded_mainloop_start;
7430 ma_proc pa_threaded_mainloop_stop;
7431 ma_proc pa_threaded_mainloop_lock;
7432 ma_proc pa_threaded_mainloop_unlock;
7433 ma_proc pa_threaded_mainloop_wait;
7434 ma_proc pa_threaded_mainloop_signal;
7435 ma_proc pa_threaded_mainloop_accept;
7436 ma_proc pa_threaded_mainloop_get_retval;
7437 ma_proc pa_threaded_mainloop_get_api;
7438 ma_proc pa_threaded_mainloop_in_thread;
7439 ma_proc pa_threaded_mainloop_set_name;
7440 ma_proc pa_context_new;
7441 ma_proc pa_context_unref;
7442 ma_proc pa_context_connect;
7443 ma_proc pa_context_disconnect;
7444 ma_proc pa_context_set_state_callback;
7445 ma_proc pa_context_get_state;
7446 ma_proc pa_context_get_sink_info_list;
7447 ma_proc pa_context_get_source_info_list;
7448 ma_proc pa_context_get_sink_info_by_name;
7449 ma_proc pa_context_get_source_info_by_name;
7450 ma_proc pa_operation_unref;
7451 ma_proc pa_operation_get_state;
7452 ma_proc pa_channel_map_init_extend;
7453 ma_proc pa_channel_map_valid;
7454 ma_proc pa_channel_map_compatible;
7455 ma_proc pa_stream_new;
7456 ma_proc pa_stream_unref;
7457 ma_proc pa_stream_connect_playback;
7458 ma_proc pa_stream_connect_record;
7459 ma_proc pa_stream_disconnect;
7460 ma_proc pa_stream_get_state;
7461 ma_proc pa_stream_get_sample_spec;
7462 ma_proc pa_stream_get_channel_map;
7463 ma_proc pa_stream_get_buffer_attr;
7464 ma_proc pa_stream_set_buffer_attr;
7465 ma_proc pa_stream_get_device_name;
7466 ma_proc pa_stream_set_write_callback;
7467 ma_proc pa_stream_set_read_callback;
7468 ma_proc pa_stream_set_suspended_callback;
7469 ma_proc pa_stream_set_moved_callback;
7470 ma_proc pa_stream_is_suspended;
7471 ma_proc pa_stream_flush;
7472 ma_proc pa_stream_drain;
7473 ma_proc pa_stream_is_corked;
7474 ma_proc pa_stream_cork;
7475 ma_proc pa_stream_trigger;
7476 ma_proc pa_stream_begin_write;
7477 ma_proc pa_stream_write;
7478 ma_proc pa_stream_peek;
7479 ma_proc pa_stream_drop;
7480 ma_proc pa_stream_writable_size;
7481 ma_proc pa_stream_readable_size;
7482
7483 /*pa_mainloop**/ ma_ptr pMainLoop;
7484 /*pa_context**/ ma_ptr pPulseContext;
7485 char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
7486 char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
7487 } pulse;
7488 #endif
7489 #ifdef MA_SUPPORT_JACK
7490 struct
7491 {
7492 ma_handle jackSO;
7493 ma_proc jack_client_open;
7494 ma_proc jack_client_close;
7495 ma_proc jack_client_name_size;
7496 ma_proc jack_set_process_callback;
7497 ma_proc jack_set_buffer_size_callback;
7498 ma_proc jack_on_shutdown;
7499 ma_proc jack_get_sample_rate;
7500 ma_proc jack_get_buffer_size;
7501 ma_proc jack_get_ports;
7502 ma_proc jack_activate;
7503 ma_proc jack_deactivate;
7504 ma_proc jack_connect;
7505 ma_proc jack_port_register;
7506 ma_proc jack_port_name;
7507 ma_proc jack_port_get_buffer;
7508 ma_proc jack_free;
7509
7510 char* pClientName;
7511 ma_bool32 tryStartServer;
7512 } jack;
7513 #endif
7514 #ifdef MA_SUPPORT_COREAUDIO
7515 struct
7516 {
7517 ma_handle hCoreFoundation;
7518 ma_proc CFStringGetCString;
7519 ma_proc CFRelease;
7520
7521 ma_handle hCoreAudio;
7522 ma_proc AudioObjectGetPropertyData;
7523 ma_proc AudioObjectGetPropertyDataSize;
7524 ma_proc AudioObjectSetPropertyData;
7525 ma_proc AudioObjectAddPropertyListener;
7526 ma_proc AudioObjectRemovePropertyListener;
7527
7528 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
7529 ma_proc AudioComponentFindNext;
7530 ma_proc AudioComponentInstanceDispose;
7531 ma_proc AudioComponentInstanceNew;
7532 ma_proc AudioOutputUnitStart;
7533 ma_proc AudioOutputUnitStop;
7534 ma_proc AudioUnitAddPropertyListener;
7535 ma_proc AudioUnitGetPropertyInfo;
7536 ma_proc AudioUnitGetProperty;
7537 ma_proc AudioUnitSetProperty;
7538 ma_proc AudioUnitInitialize;
7539 ma_proc AudioUnitRender;
7540
7541 /*AudioComponent*/ ma_ptr component;
7542 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(). */
7543 } coreaudio;
7544 #endif
7545 #ifdef MA_SUPPORT_SNDIO
7546 struct
7547 {
7548 ma_handle sndioSO;
7549 ma_proc sio_open;
7550 ma_proc sio_close;
7551 ma_proc sio_setpar;
7552 ma_proc sio_getpar;
7553 ma_proc sio_getcap;
7554 ma_proc sio_start;
7555 ma_proc sio_stop;
7556 ma_proc sio_read;
7557 ma_proc sio_write;
7558 ma_proc sio_onmove;
7559 ma_proc sio_nfds;
7560 ma_proc sio_pollfd;
7561 ma_proc sio_revents;
7562 ma_proc sio_eof;
7563 ma_proc sio_setvol;
7564 ma_proc sio_onvol;
7565 ma_proc sio_initpar;
7566 } sndio;
7567 #endif
7568 #ifdef MA_SUPPORT_AUDIO4
7569 struct
7570 {
7571 int _unused;
7572 } audio4;
7573 #endif
7574 #ifdef MA_SUPPORT_OSS
7575 struct
7576 {
7577 int versionMajor;
7578 int versionMinor;
7579 } oss;
7580 #endif
7581 #ifdef MA_SUPPORT_AAUDIO
7582 struct
7583 {
7584 ma_handle hAAudio; /* libaaudio.so */
7585 ma_proc AAudio_createStreamBuilder;
7586 ma_proc AAudioStreamBuilder_delete;
7587 ma_proc AAudioStreamBuilder_setDeviceId;
7588 ma_proc AAudioStreamBuilder_setDirection;
7589 ma_proc AAudioStreamBuilder_setSharingMode;
7590 ma_proc AAudioStreamBuilder_setFormat;
7591 ma_proc AAudioStreamBuilder_setChannelCount;
7592 ma_proc AAudioStreamBuilder_setSampleRate;
7593 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
7594 ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
7595 ma_proc AAudioStreamBuilder_setDataCallback;
7596 ma_proc AAudioStreamBuilder_setErrorCallback;
7597 ma_proc AAudioStreamBuilder_setPerformanceMode;
7598 ma_proc AAudioStreamBuilder_setUsage;
7599 ma_proc AAudioStreamBuilder_setContentType;
7600 ma_proc AAudioStreamBuilder_setInputPreset;
7601 ma_proc AAudioStreamBuilder_setAllowedCapturePolicy;
7602 ma_proc AAudioStreamBuilder_openStream;
7603 ma_proc AAudioStream_close;
7604 ma_proc AAudioStream_getState;
7605 ma_proc AAudioStream_waitForStateChange;
7606 ma_proc AAudioStream_getFormat;
7607 ma_proc AAudioStream_getChannelCount;
7608 ma_proc AAudioStream_getSampleRate;
7609 ma_proc AAudioStream_getBufferCapacityInFrames;
7610 ma_proc AAudioStream_getFramesPerDataCallback;
7611 ma_proc AAudioStream_getFramesPerBurst;
7612 ma_proc AAudioStream_requestStart;
7613 ma_proc AAudioStream_requestStop;
7614 ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */
7615 } aaudio;
7616 #endif
7617 #ifdef MA_SUPPORT_OPENSL
7618 struct
7619 {
7620 ma_handle libOpenSLES;
7621 ma_handle SL_IID_ENGINE;
7622 ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
7623 ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
7624 ma_handle SL_IID_RECORD;
7625 ma_handle SL_IID_PLAY;
7626 ma_handle SL_IID_OUTPUTMIX;
7627 ma_handle SL_IID_ANDROIDCONFIGURATION;
7628 ma_proc slCreateEngine;
7629 } opensl;
7630 #endif
7631 #ifdef MA_SUPPORT_WEBAUDIO
7632 struct
7633 {
7634 int _unused;
7635 } webaudio;
7636 #endif
7637 #ifdef MA_SUPPORT_NULL
7638 struct
7639 {
7640 int _unused;
7641 } null_backend;
7642 #endif
7643 };
7644
7645 union
7646 {
7647 #if defined(MA_WIN32)
7648 struct
7649 {
7650 /*HMODULE*/ ma_handle hOle32DLL;
7651 ma_proc CoInitialize;
7652 ma_proc CoInitializeEx;
7653 ma_proc CoUninitialize;
7654 ma_proc CoCreateInstance;
7655 ma_proc CoTaskMemFree;
7656 ma_proc PropVariantClear;
7657 ma_proc StringFromGUID2;
7658
7659 /*HMODULE*/ ma_handle hUser32DLL;
7660 ma_proc GetForegroundWindow;
7661 ma_proc GetDesktopWindow;
7662
7663 /*HMODULE*/ ma_handle hAdvapi32DLL;
7664 ma_proc RegOpenKeyExA;
7665 ma_proc RegCloseKey;
7666 ma_proc RegQueryValueExA;
7667 } win32;
7668 #endif
7669 #ifdef MA_POSIX
7670 struct
7671 {
7672 int _unused;
7673 } posix;
7674 #endif
7675 int _unused;
7676 };
7677 };
7678
7679 struct ma_device
7680 {
7681 ma_context* pContext;
7682 ma_device_type type;
7683 ma_uint32 sampleRate;
7684 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. */
7685 ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */
7686 ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */
7687 ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */
7688 void* pUserData; /* Application defined data. */
7689 ma_mutex startStopLock;
7690 ma_event wakeupEvent;
7691 ma_event startEvent;
7692 ma_event stopEvent;
7693 ma_thread thread;
7694 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
7695 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(). */
7696 ma_bool8 noPreSilencedOutputBuffer;
7697 ma_bool8 noClip;
7698 ma_bool8 noDisableDenormals;
7699 ma_bool8 noFixedSizedCallback;
7700 ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
7701 ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */
7702 struct
7703 {
7704 ma_resample_algorithm algorithm;
7705 ma_resampling_backend_vtable* pBackendVTable;
7706 void* pBackendUserData;
7707 struct
7708 {
7709 ma_uint32 lpfOrder;
7710 } linear;
7711 } resampling;
7712 struct
7713 {
7714 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
7715 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. */
7716 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
7717 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
7718 ma_format format;
7719 ma_uint32 channels;
7720 ma_channel channelMap[MA_MAX_CHANNELS];
7721 ma_format internalFormat;
7722 ma_uint32 internalChannels;
7723 ma_uint32 internalSampleRate;
7724 ma_channel internalChannelMap[MA_MAX_CHANNELS];
7725 ma_uint32 internalPeriodSizeInFrames;
7726 ma_uint32 internalPeriods;
7727 ma_channel_mix_mode channelMixMode;
7728 ma_bool32 calculateLFEFromSpatialChannels;
7729 ma_data_converter converter;
7730 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
7731 ma_uint32 intermediaryBufferCap;
7732 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
7733 void* pInputCache; /* In external format. Can be null. */
7734 ma_uint64 inputCacheCap;
7735 ma_uint64 inputCacheConsumed;
7736 ma_uint64 inputCacheRemaining;
7737 } playback;
7738 struct
7739 {
7740 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
7741 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. */
7742 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
7743 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
7744 ma_format format;
7745 ma_uint32 channels;
7746 ma_channel channelMap[MA_MAX_CHANNELS];
7747 ma_format internalFormat;
7748 ma_uint32 internalChannels;
7749 ma_uint32 internalSampleRate;
7750 ma_channel internalChannelMap[MA_MAX_CHANNELS];
7751 ma_uint32 internalPeriodSizeInFrames;
7752 ma_uint32 internalPeriods;
7753 ma_channel_mix_mode channelMixMode;
7754 ma_bool32 calculateLFEFromSpatialChannels;
7755 ma_data_converter converter;
7756 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
7757 ma_uint32 intermediaryBufferCap;
7758 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
7759 } capture;
7760
7761 union
7762 {
7763 #ifdef MA_SUPPORT_WASAPI
7764 struct
7765 {
7766 /*IAudioClient**/ ma_ptr pAudioClientPlayback;
7767 /*IAudioClient**/ ma_ptr pAudioClientCapture;
7768 /*IAudioRenderClient**/ ma_ptr pRenderClient;
7769 /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
7770 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
7771 ma_IMMNotificationClient notificationClient;
7772 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
7773 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
7774 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. */
7775 ma_uint32 actualBufferSizeInFramesCapture;
7776 ma_uint32 originalPeriodSizeInFrames;
7777 ma_uint32 originalPeriodSizeInMilliseconds;
7778 ma_uint32 originalPeriods;
7779 ma_performance_profile originalPerformanceProfile;
7780 ma_uint32 periodSizeInFramesPlayback;
7781 ma_uint32 periodSizeInFramesCapture;
7782 void* pMappedBufferCapture;
7783 ma_uint32 mappedBufferCaptureCap;
7784 ma_uint32 mappedBufferCaptureLen;
7785 void* pMappedBufferPlayback;
7786 ma_uint32 mappedBufferPlaybackCap;
7787 ma_uint32 mappedBufferPlaybackLen;
7788 ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
7789 ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
7790 ma_uint32 loopbackProcessID;
7791 ma_bool8 loopbackProcessExclude;
7792 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
7793 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
7794 ma_bool8 noHardwareOffloading;
7795 ma_bool8 allowCaptureAutoStreamRouting;
7796 ma_bool8 allowPlaybackAutoStreamRouting;
7797 ma_bool8 isDetachedPlayback;
7798 ma_bool8 isDetachedCapture;
7799 ma_wasapi_usage usage;
7800 void* hAvrtHandle;
7801 ma_mutex rerouteLock;
7802 } wasapi;
7803 #endif
7804 #ifdef MA_SUPPORT_DSOUND
7805 struct
7806 {
7807 /*LPDIRECTSOUND*/ ma_ptr pPlayback;
7808 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
7809 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
7810 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
7811 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
7812 } dsound;
7813 #endif
7814 #ifdef MA_SUPPORT_WINMM
7815 struct
7816 {
7817 /*HWAVEOUT*/ ma_handle hDevicePlayback;
7818 /*HWAVEIN*/ ma_handle hDeviceCapture;
7819 /*HANDLE*/ ma_handle hEventPlayback;
7820 /*HANDLE*/ ma_handle hEventCapture;
7821 ma_uint32 fragmentSizeInFrames;
7822 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
7823 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
7824 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
7825 ma_uint32 headerFramesConsumedCapture; /* ^^^ */
7826 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
7827 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
7828 ma_uint8* pIntermediaryBufferPlayback;
7829 ma_uint8* pIntermediaryBufferCapture;
7830 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
7831 } winmm;
7832 #endif
7833 #ifdef MA_SUPPORT_ALSA
7834 struct
7835 {
7836 /*snd_pcm_t**/ ma_ptr pPCMPlayback;
7837 /*snd_pcm_t**/ ma_ptr pPCMCapture;
7838 /*struct pollfd**/ void* pPollDescriptorsPlayback;
7839 /*struct pollfd**/ void* pPollDescriptorsCapture;
7840 int pollDescriptorCountPlayback;
7841 int pollDescriptorCountCapture;
7842 int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */
7843 int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */
7844 ma_bool8 isUsingMMapPlayback;
7845 ma_bool8 isUsingMMapCapture;
7846 } alsa;
7847 #endif
7848 #ifdef MA_SUPPORT_PULSEAUDIO
7849 struct
7850 {
7851 /*pa_mainloop**/ ma_ptr pMainLoop;
7852 /*pa_context**/ ma_ptr pPulseContext;
7853 /*pa_stream**/ ma_ptr pStreamPlayback;
7854 /*pa_stream**/ ma_ptr pStreamCapture;
7855 } pulse;
7856 #endif
7857 #ifdef MA_SUPPORT_JACK
7858 struct
7859 {
7860 /*jack_client_t**/ ma_ptr pClient;
7861 /*jack_port_t**/ ma_ptr* ppPortsPlayback;
7862 /*jack_port_t**/ ma_ptr* ppPortsCapture;
7863 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
7864 float* pIntermediaryBufferCapture;
7865 } jack;
7866 #endif
7867 #ifdef MA_SUPPORT_COREAUDIO
7868 struct
7869 {
7870 ma_uint32 deviceObjectIDPlayback;
7871 ma_uint32 deviceObjectIDCapture;
7872 /*AudioUnit*/ ma_ptr audioUnitPlayback;
7873 /*AudioUnit*/ ma_ptr audioUnitCapture;
7874 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
7875 ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */
7876 ma_event stopEvent;
7877 ma_uint32 originalPeriodSizeInFrames;
7878 ma_uint32 originalPeriodSizeInMilliseconds;
7879 ma_uint32 originalPeriods;
7880 ma_performance_profile originalPerformanceProfile;
7881 ma_bool32 isDefaultPlaybackDevice;
7882 ma_bool32 isDefaultCaptureDevice;
7883 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
7884 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
7885 void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
7886 } coreaudio;
7887 #endif
7888 #ifdef MA_SUPPORT_SNDIO
7889 struct
7890 {
7891 ma_ptr handlePlayback;
7892 ma_ptr handleCapture;
7893 ma_bool32 isStartedPlayback;
7894 ma_bool32 isStartedCapture;
7895 } sndio;
7896 #endif
7897 #ifdef MA_SUPPORT_AUDIO4
7898 struct
7899 {
7900 int fdPlayback;
7901 int fdCapture;
7902 } audio4;
7903 #endif
7904 #ifdef MA_SUPPORT_OSS
7905 struct
7906 {
7907 int fdPlayback;
7908 int fdCapture;
7909 } oss;
7910 #endif
7911 #ifdef MA_SUPPORT_AAUDIO
7912 struct
7913 {
7914 /*AAudioStream**/ ma_ptr pStreamPlayback;
7915 /*AAudioStream**/ ma_ptr pStreamCapture;
7916 ma_aaudio_usage usage;
7917 ma_aaudio_content_type contentType;
7918 ma_aaudio_input_preset inputPreset;
7919 ma_aaudio_allowed_capture_policy allowedCapturePolicy;
7920 ma_bool32 noAutoStartAfterReroute;
7921 } aaudio;
7922 #endif
7923 #ifdef MA_SUPPORT_OPENSL
7924 struct
7925 {
7926 /*SLObjectItf*/ ma_ptr pOutputMixObj;
7927 /*SLOutputMixItf*/ ma_ptr pOutputMix;
7928 /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
7929 /*SLPlayItf*/ ma_ptr pAudioPlayer;
7930 /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
7931 /*SLRecordItf*/ ma_ptr pAudioRecorder;
7932 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
7933 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
7934 ma_bool32 isDrainingCapture;
7935 ma_bool32 isDrainingPlayback;
7936 ma_uint32 currentBufferIndexPlayback;
7937 ma_uint32 currentBufferIndexCapture;
7938 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
7939 ma_uint8* pBufferCapture;
7940 } opensl;
7941 #endif
7942 #ifdef MA_SUPPORT_WEBAUDIO
7943 struct
7944 {
7945 /* AudioWorklets path. */
7946 /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextPlayback;
7947 /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextCapture;
7948 /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodePlayback;
7949 /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodeCapture;
7950 size_t intermediaryBufferSizeInFramesPlayback;
7951 size_t intermediaryBufferSizeInFramesCapture;
7952 float* pIntermediaryBufferPlayback;
7953 float* pIntermediaryBufferCapture;
7954 void* pStackBufferPlayback;
7955 void* pStackBufferCapture;
7956 ma_bool32 isInitialized;
7957
7958 /* ScriptProcessorNode path. */
7959 int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
7960 int indexCapture;
7961 } webaudio;
7962 #endif
7963 #ifdef MA_SUPPORT_NULL
7964 struct
7965 {
7966 ma_thread deviceThread;
7967 ma_event operationEvent;
7968 ma_event operationCompletionEvent;
7969 ma_semaphore operationSemaphore;
7970 ma_uint32 operation;
7971 ma_result operationResult;
7972 ma_timer timer;
7973 double priorRunTime;
7974 ma_uint32 currentPeriodFramesRemainingPlayback;
7975 ma_uint32 currentPeriodFramesRemainingCapture;
7976 ma_uint64 lastProcessedFramePlayback;
7977 ma_uint64 lastProcessedFrameCapture;
7978 ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
7979 } null_device;
7980 #endif
7981 };
7982 };
7983 #if defined(_MSC_VER) && !defined(__clang__)
7984 #pragma warning(pop)
7985 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
7986 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
7987 #endif
7988
7989 /*
7990 Initializes a `ma_context_config` object.
7991
7992
7993 Return Value
7994 ------------
7995 A `ma_context_config` initialized to defaults.
7996
7997
7998 Remarks
7999 -------
8000 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
8001 is updated and new members are added to `ma_context_config`. It also sets logical defaults.
8002
8003 You can override members of the returned object by changing it's members directly.
8004
8005
8006 See Also
8007 --------
8008 ma_context_init()
8009 */
8010 MA_API ma_context_config ma_context_config_init(void);
8011
8012 /*
8013 Initializes a context.
8014
8015 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
8016 device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
8017
8018
8019 Parameters
8020 ----------
8021 backends (in, optional)
8022 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
8023
8024 backendCount (in, optional)
8025 The number of items in `backend`. Ignored if `backend` is NULL.
8026
8027 pConfig (in, optional)
8028 The context configuration.
8029
8030 pContext (in)
8031 A pointer to the context object being initialized.
8032
8033
8034 Return Value
8035 ------------
8036 MA_SUCCESS if successful; any other error code otherwise.
8037
8038
8039 Thread Safety
8040 -------------
8041 Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
8042
8043
8044 Remarks
8045 -------
8046 When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
8047
8048 |-------------|-----------------------|--------------------------------------------------------|
8049 | Name | Enum Name | Supported Operating Systems |
8050 |-------------|-----------------------|--------------------------------------------------------|
8051 | WASAPI | ma_backend_wasapi | Windows Vista+ |
8052 | DirectSound | ma_backend_dsound | Windows XP+ |
8053 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
8054 | Core Audio | ma_backend_coreaudio | macOS, iOS |
8055 | ALSA | ma_backend_alsa | Linux |
8056 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
8057 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
8058 | sndio | ma_backend_sndio | OpenBSD |
8059 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
8060 | OSS | ma_backend_oss | FreeBSD |
8061 | AAudio | ma_backend_aaudio | Android 8+ |
8062 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
8063 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
8064 | Null | ma_backend_null | Cross Platform (not used on Web) |
8065 |-------------|-----------------------|--------------------------------------------------------|
8066
8067 The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
8068 can then be set directly on the structure. Below are the members of the `ma_context_config` object.
8069
8070 pLog
8071 A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not
8072 require logging. See the `ma_log` API for details on how to use the logging system.
8073
8074 threadPriority
8075 The desired priority to use for the audio thread. Allowable values include the following:
8076
8077 |--------------------------------------|
8078 | Thread Priority |
8079 |--------------------------------------|
8080 | ma_thread_priority_idle |
8081 | ma_thread_priority_lowest |
8082 | ma_thread_priority_low |
8083 | ma_thread_priority_normal |
8084 | ma_thread_priority_high |
8085 | ma_thread_priority_highest (default) |
8086 | ma_thread_priority_realtime |
8087 | ma_thread_priority_default |
8088 |--------------------------------------|
8089
8090 threadStackSize
8091 The desired size of the stack for the audio thread. Defaults to the operating system's default.
8092
8093 pUserData
8094 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
8095
8096 allocationCallbacks
8097 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
8098 callbacks will be used for anything tied to the context, including devices.
8099
8100 alsa.useVerboseDeviceEnumeration
8101 ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique
8102 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
8103 it so the ALSA backend includes all devices. Defaults to false.
8104
8105 pulse.pApplicationName
8106 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
8107
8108 pulse.pServerName
8109 PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
8110
8111 pulse.tryAutoSpawn
8112 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
8113 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
8114 intrusive for the end user.
8115
8116 coreaudio.sessionCategory
8117 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
8118
8119 |-----------------------------------------|-------------------------------------|
8120 | miniaudio Token | Core Audio Token |
8121 |-----------------------------------------|-------------------------------------|
8122 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
8123 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
8124 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
8125 | ma_ios_session_category_record | AVAudioSessionCategoryRecord |
8126 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
8127 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
8128 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
8129 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
8130 |-----------------------------------------|-------------------------------------|
8131
8132 coreaudio.sessionCategoryOptions
8133 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
8134
8135 |---------------------------------------------------------------------------|------------------------------------------------------------------|
8136 | miniaudio Token | Core Audio Token |
8137 |---------------------------------------------------------------------------|------------------------------------------------------------------|
8138 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
8139 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
8140 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
8141 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
8142 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
8143 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
8144 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
8145 |---------------------------------------------------------------------------|------------------------------------------------------------------|
8146
8147 coreaudio.noAudioSessionActivate
8148 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization.
8149
8150 coreaudio.noAudioSessionDeactivate
8151 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization.
8152
8153 jack.pClientName
8154 The name of the client to pass to `jack_client_open()`.
8155
8156 jack.tryStartServer
8157 Whether or not to try auto-starting the JACK server. Defaults to false.
8158
8159
8160 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
8161 relevant backends every time it's initialized.
8162
8163 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
8164 reason for this is that a pointer to the context is stored in the `ma_device` structure.
8165
8166
8167 Example 1 - Default Initialization
8168 ----------------------------------
8169 The example below shows how to initialize the context using the default configuration.
8170
8171 ```c
8172 ma_context context;
8173 ma_result result = ma_context_init(NULL, 0, NULL, &context);
8174 if (result != MA_SUCCESS) {
8175 // Error.
8176 }
8177 ```
8178
8179
8180 Example 2 - Custom Configuration
8181 --------------------------------
8182 The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
8183 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
8184 want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
8185
8186 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.
8187
8188 ```c
8189 ma_backend backends[] = {
8190 ma_backend_alsa,
8191 ma_backend_pulseaudio,
8192 ma_backend_wasapi,
8193 ma_backend_dsound
8194 };
8195
8196 ma_log log;
8197 ma_log_init(&log);
8198 ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData));
8199
8200 ma_context_config config = ma_context_config_init();
8201 config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured.
8202
8203 ma_context context;
8204 ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
8205 if (result != MA_SUCCESS) {
8206 // Error.
8207 if (result == MA_NO_BACKEND) {
8208 // Couldn't find an appropriate backend.
8209 }
8210 }
8211
8212 // You could also attach a log callback post-initialization:
8213 ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData));
8214 ```
8215
8216
8217 See Also
8218 --------
8219 ma_context_config_init()
8220 ma_context_uninit()
8221 */
8222 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
8223
8224 /*
8225 Uninitializes a context.
8226
8227
8228 Return Value
8229 ------------
8230 MA_SUCCESS if successful; any other error code otherwise.
8231
8232
8233 Thread Safety
8234 -------------
8235 Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
8236
8237
8238 Remarks
8239 -------
8240 Results are undefined if you call this while any device created by this context is still active.
8241
8242
8243 See Also
8244 --------
8245 ma_context_init()
8246 */
8247 MA_API ma_result ma_context_uninit(ma_context* pContext);
8248
8249 /*
8250 Retrieves the size of the ma_context object.
8251
8252 This is mainly for the purpose of bindings to know how much memory to allocate.
8253 */
8254 MA_API size_t ma_context_sizeof(void);
8255
8256 /*
8257 Retrieves a pointer to the log object associated with this context.
8258
8259
8260 Remarks
8261 -------
8262 Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log
8263 message.
8264
8265 You can attach your own logging callback to the log with `ma_log_register_callback()`
8266
8267
8268 Return Value
8269 ------------
8270 A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs,
8271 NULL will be returned.
8272 */
8273 MA_API ma_log* ma_context_get_log(ma_context* pContext);
8274
8275 /*
8276 Enumerates over every device (both playback and capture).
8277
8278 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
8279 an internal heap allocation, or it simply suits your code better.
8280
8281 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
8282 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,
8283 but don't call it from within the enumeration callback.
8284
8285 Returning false from the callback will stop enumeration. Returning true will continue enumeration.
8286
8287
8288 Parameters
8289 ----------
8290 pContext (in)
8291 A pointer to the context performing the enumeration.
8292
8293 callback (in)
8294 The callback to fire for each enumerated device.
8295
8296 pUserData (in)
8297 A pointer to application-defined data passed to the callback.
8298
8299
8300 Return Value
8301 ------------
8302 MA_SUCCESS if successful; any other error code otherwise.
8303
8304
8305 Thread Safety
8306 -------------
8307 Safe. This is guarded using a simple mutex lock.
8308
8309
8310 Remarks
8311 -------
8312 Do _not_ assume the first enumerated device of a given type is the default device.
8313
8314 Some backends and platforms may only support default playback and capture devices.
8315
8316 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,
8317 do not try to call `ma_context_get_device_info()` from within the callback.
8318
8319 Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
8320
8321
8322 Example 1 - Simple Enumeration
8323 ------------------------------
8324 ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
8325 {
8326 printf("Device Name: %s\n", pInfo->name);
8327 return MA_TRUE;
8328 }
8329
8330 ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
8331 if (result != MA_SUCCESS) {
8332 // Error.
8333 }
8334
8335
8336 See Also
8337 --------
8338 ma_context_get_devices()
8339 */
8340 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
8341
8342 /*
8343 Retrieves basic information about every active playback and/or capture device.
8344
8345 This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
8346 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.
8347
8348
8349 Parameters
8350 ----------
8351 pContext (in)
8352 A pointer to the context performing the enumeration.
8353
8354 ppPlaybackDeviceInfos (out)
8355 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
8356
8357 pPlaybackDeviceCount (out)
8358 A pointer to an unsigned integer that will receive the number of playback devices.
8359
8360 ppCaptureDeviceInfos (out)
8361 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
8362
8363 pCaptureDeviceCount (out)
8364 A pointer to an unsigned integer that will receive the number of capture devices.
8365
8366
8367 Return Value
8368 ------------
8369 MA_SUCCESS if successful; any other error code otherwise.
8370
8371
8372 Thread Safety
8373 -------------
8374 Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
8375 threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
8376
8377
8378 Remarks
8379 -------
8380 It is _not_ safe to assume the first device in the list is the default device.
8381
8382 You can pass in NULL for the playback or capture lists in which case they'll be ignored.
8383
8384 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.
8385
8386
8387 See Also
8388 --------
8389 ma_context_get_devices()
8390 */
8391 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);
8392
8393 /*
8394 Retrieves information about a device of the given type, with the specified ID and share mode.
8395
8396
8397 Parameters
8398 ----------
8399 pContext (in)
8400 A pointer to the context performing the query.
8401
8402 deviceType (in)
8403 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
8404
8405 pDeviceID (in)
8406 The ID of the device being queried.
8407
8408 pDeviceInfo (out)
8409 A pointer to the `ma_device_info` structure that will receive the device information.
8410
8411
8412 Return Value
8413 ------------
8414 MA_SUCCESS if successful; any other error code otherwise.
8415
8416
8417 Thread Safety
8418 -------------
8419 Safe. This is guarded using a simple mutex lock.
8420
8421
8422 Remarks
8423 -------
8424 Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
8425
8426 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
8427 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
8428 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
8429 the requested share mode is unsupported.
8430
8431 This leaves pDeviceInfo unmodified in the result of an error.
8432 */
8433 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);
8434
8435 /*
8436 Determines if the given context supports loopback mode.
8437
8438
8439 Parameters
8440 ----------
8441 pContext (in)
8442 A pointer to the context getting queried.
8443
8444
8445 Return Value
8446 ------------
8447 MA_TRUE if the context supports loopback mode; MA_FALSE otherwise.
8448 */
8449 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext);
8450
8451
8452
8453 /*
8454 Initializes a device config with default settings.
8455
8456
8457 Parameters
8458 ----------
8459 deviceType (in)
8460 The type of the device this config is being initialized for. This must set to one of the following:
8461
8462 |-------------------------|
8463 | Device Type |
8464 |-------------------------|
8465 | ma_device_type_playback |
8466 | ma_device_type_capture |
8467 | ma_device_type_duplex |
8468 | ma_device_type_loopback |
8469 |-------------------------|
8470
8471
8472 Return Value
8473 ------------
8474 A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.
8475
8476
8477 Thread Safety
8478 -------------
8479 Safe.
8480
8481
8482 Callback Safety
8483 ---------------
8484 Safe, but don't try initializing a device in a callback.
8485
8486
8487 Remarks
8488 -------
8489 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
8490 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
8491 before initializing the device.
8492
8493 See `ma_device_init()` for details on specific configuration options.
8494
8495
8496 Example 1 - Simple Configuration
8497 --------------------------------
8498 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
8499 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
8500 to the `ma_device_config` structure.
8501
8502 ```c
8503 ma_device_config config = ma_device_config_init(ma_device_type_playback);
8504 config.playback.format = ma_format_f32;
8505 config.playback.channels = 2;
8506 config.sampleRate = 48000;
8507 config.dataCallback = ma_data_callback;
8508 config.pUserData = pMyUserData;
8509 ```
8510
8511
8512 See Also
8513 --------
8514 ma_device_init()
8515 ma_device_init_ex()
8516 */
8517 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType);
8518
8519
8520 /*
8521 Initializes a device.
8522
8523 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
8524 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
8525 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
8526 device is done via a callback which is fired by miniaudio at periodic time intervals.
8527
8528 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
8529 or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
8530 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
8531 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
8532 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
8533 backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for.
8534
8535 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
8536 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
8537 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.
8538
8539
8540 Parameters
8541 ----------
8542 pContext (in, optional)
8543 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.
8544
8545 pConfig (in)
8546 A pointer to the device configuration. Cannot be null. See remarks for details.
8547
8548 pDevice (out)
8549 A pointer to the device object being initialized.
8550
8551
8552 Return Value
8553 ------------
8554 MA_SUCCESS if successful; any other error code otherwise.
8555
8556
8557 Thread Safety
8558 -------------
8559 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
8560 calling this at the same time as `ma_device_uninit()`.
8561
8562
8563 Callback Safety
8564 ---------------
8565 Unsafe. It is not safe to call this inside any callback.
8566
8567
8568 Remarks
8569 -------
8570 Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:
8571
8572 ```c
8573 ma_context_init(NULL, 0, NULL, &context);
8574 ```
8575
8576 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
8577 device.pContext for the initialization of other devices.
8578
8579 The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can
8580 then be set directly on the structure. Below are the members of the `ma_device_config` object.
8581
8582 deviceType
8583 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.
8584
8585 sampleRate
8586 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.
8587
8588 periodSizeInFrames
8589 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
8590 be used depending on the selected performance profile. This value affects latency. See below for details.
8591
8592 periodSizeInMilliseconds
8593 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
8594 used depending on the selected performance profile. The value affects latency. See below for details.
8595
8596 periods
8597 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by
8598 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.
8599
8600 performanceProfile
8601 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
8602 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
8603
8604 noPreSilencedOutputBuffer
8605 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
8606 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
8607 callback will write to every sample in the output buffer, or if you are doing your own clearing.
8608
8609 noClip
8610 When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the
8611 contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only
8612 applies when the playback sample format is f32.
8613
8614 noDisableDenormals
8615 By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals.
8616
8617 noFixedSizedCallback
8618 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
8619 consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with
8620 whatever the backend requests, which could be anything.
8621
8622 dataCallback
8623 The callback to fire whenever data is ready to be delivered to or from the device.
8624
8625 notificationCallback
8626 The callback to fire when something has changed with the device, such as whether or not it has been started or stopped.
8627
8628 pUserData
8629 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
8630
8631 resampling.algorithm
8632 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The
8633 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`.
8634
8635 resampling.pBackendVTable
8636 A pointer to an optional vtable that can be used for plugging in a custom resampler.
8637
8638 resampling.pBackendUserData
8639 A pointer that will passed to callbacks in pBackendVTable.
8640
8641 resampling.linear.lpfOrder
8642 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
8643 the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
8644 `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
8645
8646 playback.pDeviceID
8647 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
8648 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
8649
8650 playback.format
8651 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
8652 initialization from the device object directly with `device.playback.format`.
8653
8654 playback.channels
8655 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
8656 from the device object directly with `device.playback.channels`.
8657
8658 playback.pChannelMap
8659 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
8660 device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items.
8661
8662 playback.shareMode
8663 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
8664 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
8665 ma_share_mode_shared and reinitializing.
8666
8667 capture.pDeviceID
8668 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
8669 default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
8670
8671 capture.format
8672 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
8673 initialization from the device object directly with `device.capture.format`.
8674
8675 capture.channels
8676 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
8677 from the device object directly with `device.capture.channels`.
8678
8679 capture.pChannelMap
8680 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
8681 device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items.
8682
8683 capture.shareMode
8684 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
8685 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
8686 ma_share_mode_shared and reinitializing.
8687
8688 wasapi.noAutoConvertSRC
8689 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.
8690
8691 wasapi.noDefaultQualitySRC
8692 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.
8693 You should usually leave this set to false, which is the default.
8694
8695 wasapi.noAutoStreamRouting
8696 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.
8697
8698 wasapi.noHardwareOffloading
8699 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.
8700
8701 alsa.noMMap
8702 ALSA only. When set to true, disables MMap mode. Defaults to false.
8703
8704 alsa.noAutoFormat
8705 ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false.
8706
8707 alsa.noAutoChannels
8708 ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false.
8709
8710 alsa.noAutoResample
8711 ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.
8712
8713 pulse.pStreamNamePlayback
8714 PulseAudio only. Sets the stream name for playback.
8715
8716 pulse.pStreamNameCapture
8717 PulseAudio only. Sets the stream name for capture.
8718
8719 coreaudio.allowNominalSampleRateChange
8720 Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
8721 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
8722 that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will
8723 find the closest match between the sample rate requested in the device config and the sample rates natively supported by the
8724 hardware. When set to false, the sample rate currently set by the operating system will always be used.
8725
8726 opensl.streamType
8727 OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the
8728 stream type will be left unset. Think of this as the type of audio you're playing.
8729
8730 opensl.recordingPreset
8731 OpenSL only. Explicitly sets the type of recording your program will be doing. When left
8732 unset, the recording preset will be left unchanged.
8733
8734 aaudio.usage
8735 AAudio only. Explicitly sets the nature of the audio the program will be consuming. When
8736 left unset, the usage will be left unchanged.
8737
8738 aaudio.contentType
8739 AAudio only. Sets the content type. When left unset, the content type will be left unchanged.
8740
8741 aaudio.inputPreset
8742 AAudio only. Explicitly sets the type of recording your program will be doing. When left
8743 unset, the input preset will be left unchanged.
8744
8745 aaudio.noAutoStartAfterReroute
8746 AAudio only. Controls whether or not the device should be automatically restarted after a
8747 stream reroute. When set to false (default) the device will be restarted automatically;
8748 otherwise the device will be stopped.
8749
8750
8751 Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
8752
8753 After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
8754
8755 If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
8756 `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
8757 `ma_performance_profile_conservative`.
8758
8759 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
8760 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
8761 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,
8762 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.
8763 Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
8764
8765 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
8766 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
8767 on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
8768 `playback/capture.channels` and `sampleRate` members of the device object.
8769
8770 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
8771 asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
8772
8773 ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
8774 If these fail it will try falling back to the "hw" device.
8775
8776
8777 Example 1 - Simple Initialization
8778 ---------------------------------
8779 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
8780 playback device this is usually all you need.
8781
8782 ```c
8783 ma_device_config config = ma_device_config_init(ma_device_type_playback);
8784 config.playback.format = ma_format_f32;
8785 config.playback.channels = 2;
8786 config.sampleRate = 48000;
8787 config.dataCallback = ma_data_callback;
8788 config.pMyUserData = pMyUserData;
8789
8790 ma_device device;
8791 ma_result result = ma_device_init(NULL, &config, &device);
8792 if (result != MA_SUCCESS) {
8793 // Error
8794 }
8795 ```
8796
8797
8798 Example 2 - Advanced Initialization
8799 -----------------------------------
8800 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
8801 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
8802 enumeration.
8803
8804 ```c
8805 ma_context context;
8806 ma_result result = ma_context_init(NULL, 0, NULL, &context);
8807 if (result != MA_SUCCESS) {
8808 // Error
8809 }
8810
8811 ma_device_info* pPlaybackDeviceInfos;
8812 ma_uint32 playbackDeviceCount;
8813 result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
8814 if (result != MA_SUCCESS) {
8815 // Error
8816 }
8817
8818 // ... choose a device from pPlaybackDeviceInfos ...
8819
8820 ma_device_config config = ma_device_config_init(ma_device_type_playback);
8821 config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
8822 config.playback.format = ma_format_f32;
8823 config.playback.channels = 2;
8824 config.sampleRate = 48000;
8825 config.dataCallback = ma_data_callback;
8826 config.pUserData = pMyUserData;
8827 config.periodSizeInMilliseconds = 10;
8828 config.periods = 3;
8829
8830 ma_device device;
8831 result = ma_device_init(&context, &config, &device);
8832 if (result != MA_SUCCESS) {
8833 // Error
8834 }
8835 ```
8836
8837
8838 See Also
8839 --------
8840 ma_device_config_init()
8841 ma_device_uninit()
8842 ma_device_start()
8843 ma_context_init()
8844 ma_context_get_devices()
8845 ma_context_enumerate_devices()
8846 */
8847 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
8848
8849 /*
8850 Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.
8851
8852 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
8853 allows you to configure the internally created context.
8854
8855
8856 Parameters
8857 ----------
8858 backends (in, optional)
8859 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
8860
8861 backendCount (in, optional)
8862 The number of items in `backend`. Ignored if `backend` is NULL.
8863
8864 pContextConfig (in, optional)
8865 The context configuration.
8866
8867 pConfig (in)
8868 A pointer to the device configuration. Cannot be null. See remarks for details.
8869
8870 pDevice (out)
8871 A pointer to the device object being initialized.
8872
8873
8874 Return Value
8875 ------------
8876 MA_SUCCESS if successful; any other error code otherwise.
8877
8878
8879 Thread Safety
8880 -------------
8881 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
8882 calling this at the same time as `ma_device_uninit()`.
8883
8884
8885 Callback Safety
8886 ---------------
8887 Unsafe. It is not safe to call this inside any callback.
8888
8889
8890 Remarks
8891 -------
8892 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
8893 your own context.
8894
8895 See the documentation for `ma_context_init()` for information on the different context configuration options.
8896
8897
8898 See Also
8899 --------
8900 ma_device_init()
8901 ma_device_uninit()
8902 ma_device_config_init()
8903 ma_context_init()
8904 */
8905 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);
8906
8907 /*
8908 Uninitializes a device.
8909
8910 This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.
8911
8912
8913 Parameters
8914 ----------
8915 pDevice (in)
8916 A pointer to the device to stop.
8917
8918
8919 Return Value
8920 ------------
8921 Nothing
8922
8923
8924 Thread Safety
8925 -------------
8926 Unsafe. As soon as this API is called the device should be considered undefined.
8927
8928
8929 Callback Safety
8930 ---------------
8931 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
8932
8933
8934 See Also
8935 --------
8936 ma_device_init()
8937 ma_device_stop()
8938 */
8939 MA_API void ma_device_uninit(ma_device* pDevice);
8940
8941
8942 /*
8943 Retrieves a pointer to the context that owns the given device.
8944 */
8945 MA_API ma_context* ma_device_get_context(ma_device* pDevice);
8946
8947 /*
8948 Helper function for retrieving the log object associated with the context that owns this device.
8949 */
8950 MA_API ma_log* ma_device_get_log(ma_device* pDevice);
8951
8952
8953 /*
8954 Retrieves information about the device.
8955
8956
8957 Parameters
8958 ----------
8959 pDevice (in)
8960 A pointer to the device whose information is being retrieved.
8961
8962 type (in)
8963 The device type. This parameter is required for duplex devices. When retrieving device
8964 information, you are doing so for an individual playback or capture device.
8965
8966 pDeviceInfo (out)
8967 A pointer to the `ma_device_info` that will receive the device information.
8968
8969
8970 Return Value
8971 ------------
8972 MA_SUCCESS if successful; any other error code otherwise.
8973
8974
8975 Thread Safety
8976 -------------
8977 Unsafe. This should be considered unsafe because it may be calling into the backend which may or
8978 may not be safe.
8979
8980
8981 Callback Safety
8982 ---------------
8983 Unsafe. You should avoid calling this in the data callback because it may call into the backend
8984 which may or may not be safe.
8985 */
8986 MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo);
8987
8988
8989 /*
8990 Retrieves the name of the device.
8991
8992
8993 Parameters
8994 ----------
8995 pDevice (in)
8996 A pointer to the device whose information is being retrieved.
8997
8998 type (in)
8999 The device type. This parameter is required for duplex devices. When retrieving device
9000 information, you are doing so for an individual playback or capture device.
9001
9002 pName (out)
9003 A pointer to the buffer that will receive the name.
9004
9005 nameCap (in)
9006 The capacity of the output buffer, including space for the null terminator.
9007
9008 pLengthNotIncludingNullTerminator (out, optional)
9009 A pointer to the variable that will receive the length of the name, not including the null
9010 terminator.
9011
9012
9013 Return Value
9014 ------------
9015 MA_SUCCESS if successful; any other error code otherwise.
9016
9017
9018 Thread Safety
9019 -------------
9020 Unsafe. This should be considered unsafe because it may be calling into the backend which may or
9021 may not be safe.
9022
9023
9024 Callback Safety
9025 ---------------
9026 Unsafe. You should avoid calling this in the data callback because it may call into the backend
9027 which may or may not be safe.
9028
9029
9030 Remarks
9031 -------
9032 If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to
9033 `pName` if you want to first get the length of the name for the purpose of memory allocation of the
9034 output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for
9035 most cases and will avoid the need for the inefficiency of calling this function twice.
9036
9037 This is implemented in terms of `ma_device_get_info()`.
9038 */
9039 MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator);
9040
9041
9042 /*
9043 Starts the device. For playback devices this begins playback. For capture devices it begins recording.
9044
9045 Use `ma_device_stop()` to stop the device.
9046
9047
9048 Parameters
9049 ----------
9050 pDevice (in)
9051 A pointer to the device to start.
9052
9053
9054 Return Value
9055 ------------
9056 MA_SUCCESS if successful; any other error code otherwise.
9057
9058
9059 Thread Safety
9060 -------------
9061 Safe. It's safe to call this from any thread with the exception of the callback thread.
9062
9063
9064 Callback Safety
9065 ---------------
9066 Unsafe. It is not safe to call this inside any callback.
9067
9068
9069 Remarks
9070 -------
9071 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
9072 audio data in the buffer, which needs to be done before the device begins playback.
9073
9074 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.
9075
9076 Do not call this in any callback.
9077
9078
9079 See Also
9080 --------
9081 ma_device_stop()
9082 */
9083 MA_API ma_result ma_device_start(ma_device* pDevice);
9084
9085 /*
9086 Stops the device. For playback devices this stops playback. For capture devices it stops recording.
9087
9088 Use `ma_device_start()` to start the device again.
9089
9090
9091 Parameters
9092 ----------
9093 pDevice (in)
9094 A pointer to the device to stop.
9095
9096
9097 Return Value
9098 ------------
9099 MA_SUCCESS if successful; any other error code otherwise.
9100
9101
9102 Thread Safety
9103 -------------
9104 Safe. It's safe to call this from any thread with the exception of the callback thread.
9105
9106
9107 Callback Safety
9108 ---------------
9109 Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
9110
9111
9112 Remarks
9113 -------
9114 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
9115 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
9116 that was specified at initialization time).
9117
9118 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
9119 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
9120 speakers or received from the microphone which can in turn result in de-syncs.
9121
9122 Do not call this in any callback.
9123
9124 This will be called implicitly by `ma_device_uninit()`.
9125
9126
9127 See Also
9128 --------
9129 ma_device_start()
9130 */
9131 MA_API ma_result ma_device_stop(ma_device* pDevice);
9132
9133 /*
9134 Determines whether or not the device is started.
9135
9136
9137 Parameters
9138 ----------
9139 pDevice (in)
9140 A pointer to the device whose start state is being retrieved.
9141
9142
9143 Return Value
9144 ------------
9145 True if the device is started, false otherwise.
9146
9147
9148 Thread Safety
9149 -------------
9150 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
9151 value will be out of sync.
9152
9153
9154 Callback Safety
9155 ---------------
9156 Safe. This is implemented as a simple accessor.
9157
9158
9159 See Also
9160 --------
9161 ma_device_start()
9162 ma_device_stop()
9163 */
9164 MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice);
9165
9166
9167 /*
9168 Retrieves the state of the device.
9169
9170
9171 Parameters
9172 ----------
9173 pDevice (in)
9174 A pointer to the device whose state is being retrieved.
9175
9176
9177 Return Value
9178 ------------
9179 The current state of the device. The return value will be one of the following:
9180
9181 +-------------------------------+------------------------------------------------------------------------------+
9182 | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. |
9183 +-------------------------------+------------------------------------------------------------------------------+
9184 | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. |
9185 +-------------------------------+------------------------------------------------------------------------------+
9186 | ma_device_state_started | The device started and requesting and/or delivering audio data. |
9187 +-------------------------------+------------------------------------------------------------------------------+
9188 | ma_device_state_starting | The device is in the process of starting. |
9189 +-------------------------------+------------------------------------------------------------------------------+
9190 | ma_device_state_stopping | The device is in the process of stopping. |
9191 +-------------------------------+------------------------------------------------------------------------------+
9192
9193
9194 Thread Safety
9195 -------------
9196 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,
9197 there's a possibility the return value could be out of sync. See remarks.
9198
9199
9200 Callback Safety
9201 ---------------
9202 Safe. This is implemented as a simple accessor.
9203
9204
9205 Remarks
9206 -------
9207 The general flow of a devices state goes like this:
9208
9209 ```
9210 ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped
9211 ma_device_start() -> ma_device_state_starting -> ma_device_state_started
9212 ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped
9213 ```
9214
9215 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
9216 value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own
9217 synchronization.
9218 */
9219 MA_API ma_device_state ma_device_get_state(const ma_device* pDevice);
9220
9221
9222 /*
9223 Performs post backend initialization routines for setting up internal data conversion.
9224
9225 This should be called whenever the backend is initialized. The only time this should be called from
9226 outside of miniaudio is if you're implementing a custom backend, and you would only do it if you
9227 are reinitializing the backend due to rerouting or reinitializing for some reason.
9228
9229
9230 Parameters
9231 ----------
9232 pDevice [in]
9233 A pointer to the device.
9234
9235 deviceType [in]
9236 The type of the device that was just reinitialized.
9237
9238 pPlaybackDescriptor [in]
9239 The descriptor of the playback device containing the internal data format and buffer sizes.
9240
9241 pPlaybackDescriptor [in]
9242 The descriptor of the capture device containing the internal data format and buffer sizes.
9243
9244
9245 Return Value
9246 ------------
9247 MA_SUCCESS if successful; any other error otherwise.
9248
9249
9250 Thread Safety
9251 -------------
9252 Unsafe. This will be reinitializing internal data converters which may be in use by another thread.
9253
9254
9255 Callback Safety
9256 ---------------
9257 Unsafe. This will be reinitializing internal data converters which may be in use by the callback.
9258
9259
9260 Remarks
9261 -------
9262 For a duplex device, you can call this for only one side of the system. This is why the deviceType
9263 is specified as a parameter rather than deriving it from the device.
9264
9265 You do not need to call this manually unless you are doing a custom backend, in which case you need
9266 only do it if you're manually performing rerouting or reinitialization.
9267 */
9268 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);
9269
9270
9271 /*
9272 Sets the master volume factor for the device.
9273
9274 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
9275 values less than 0 decreases the volume.
9276
9277
9278 Parameters
9279 ----------
9280 pDevice (in)
9281 A pointer to the device whose volume is being set.
9282
9283 volume (in)
9284 The new volume factor. Must be >= 0.
9285
9286
9287 Return Value
9288 ------------
9289 MA_SUCCESS if the volume was set successfully.
9290 MA_INVALID_ARGS if pDevice is NULL.
9291 MA_INVALID_ARGS if volume is negative.
9292
9293
9294 Thread Safety
9295 -------------
9296 Safe. This just sets a local member of the device object.
9297
9298
9299 Callback Safety
9300 ---------------
9301 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
9302
9303
9304 Remarks
9305 -------
9306 This applies the volume factor across all channels.
9307
9308 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
9309
9310
9311 See Also
9312 --------
9313 ma_device_get_master_volume()
9314 ma_device_set_master_volume_db()
9315 ma_device_get_master_volume_db()
9316 */
9317 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume);
9318
9319 /*
9320 Retrieves the master volume factor for the device.
9321
9322
9323 Parameters
9324 ----------
9325 pDevice (in)
9326 A pointer to the device whose volume factor is being retrieved.
9327
9328 pVolume (in)
9329 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].
9330
9331
9332 Return Value
9333 ------------
9334 MA_SUCCESS if successful.
9335 MA_INVALID_ARGS if pDevice is NULL.
9336 MA_INVALID_ARGS if pVolume is NULL.
9337
9338
9339 Thread Safety
9340 -------------
9341 Safe. This just a simple member retrieval.
9342
9343
9344 Callback Safety
9345 ---------------
9346 Safe.
9347
9348
9349 Remarks
9350 -------
9351 If an error occurs, `*pVolume` will be set to 0.
9352
9353
9354 See Also
9355 --------
9356 ma_device_set_master_volume()
9357 ma_device_set_master_volume_gain_db()
9358 ma_device_get_master_volume_gain_db()
9359 */
9360 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume);
9361
9362 /*
9363 Sets the master volume for the device as gain in decibels.
9364
9365 A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
9366
9367
9368 Parameters
9369 ----------
9370 pDevice (in)
9371 A pointer to the device whose gain is being set.
9372
9373 gainDB (in)
9374 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.
9375
9376
9377 Return Value
9378 ------------
9379 MA_SUCCESS if the volume was set successfully.
9380 MA_INVALID_ARGS if pDevice is NULL.
9381 MA_INVALID_ARGS if the gain is > 0.
9382
9383
9384 Thread Safety
9385 -------------
9386 Safe. This just sets a local member of the device object.
9387
9388
9389 Callback Safety
9390 ---------------
9391 Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
9392
9393
9394 Remarks
9395 -------
9396 This applies the gain across all channels.
9397
9398 This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
9399
9400
9401 See Also
9402 --------
9403 ma_device_get_master_volume_gain_db()
9404 ma_device_set_master_volume()
9405 ma_device_get_master_volume()
9406 */
9407 MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB);
9408
9409 /*
9410 Retrieves the master gain in decibels.
9411
9412
9413 Parameters
9414 ----------
9415 pDevice (in)
9416 A pointer to the device whose gain is being retrieved.
9417
9418 pGainDB (in)
9419 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.
9420
9421
9422 Return Value
9423 ------------
9424 MA_SUCCESS if successful.
9425 MA_INVALID_ARGS if pDevice is NULL.
9426 MA_INVALID_ARGS if pGainDB is NULL.
9427
9428
9429 Thread Safety
9430 -------------
9431 Safe. This just a simple member retrieval.
9432
9433
9434 Callback Safety
9435 ---------------
9436 Safe.
9437
9438
9439 Remarks
9440 -------
9441 If an error occurs, `*pGainDB` will be set to 0.
9442
9443
9444 See Also
9445 --------
9446 ma_device_set_master_volume_db()
9447 ma_device_set_master_volume()
9448 ma_device_get_master_volume()
9449 */
9450 MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB);
9451
9452
9453 /*
9454 Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback.
9455
9456
9457 Parameters
9458 ----------
9459 pDevice (in)
9460 A pointer to device whose processing the data callback.
9461
9462 pOutput (out)
9463 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
9464 this can be NULL, in which case pInput must not be NULL.
9465
9466 pInput (in)
9467 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
9468 NULL, in which case `pOutput` must not be NULL.
9469
9470 frameCount (in)
9471 The number of frames being processed.
9472
9473
9474 Return Value
9475 ------------
9476 MA_SUCCESS if successful; any other result code otherwise.
9477
9478
9479 Thread Safety
9480 -------------
9481 This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a
9482 playback and capture device in duplex setups.
9483
9484
9485 Callback Safety
9486 ---------------
9487 Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.
9488
9489
9490 Remarks
9491 -------
9492 If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in
9493 which case `pInput` will be processed first, followed by `pOutput`.
9494
9495 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
9496 callback.
9497 */
9498 MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
9499
9500
9501 /*
9502 Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile.
9503
9504 This function is used by backends for helping determine an appropriately sized buffer to use with
9505 the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the
9506 `pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a
9507 best guess at the device's native sample rate is also required which is where `nativeSampleRate`
9508 comes in. In addition, the performance profile is also needed for cases where both the period size
9509 in frames and milliseconds are both zero.
9510
9511
9512 Parameters
9513 ----------
9514 pDescriptor (in)
9515 A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members
9516 will be used for the calculation of the buffer size.
9517
9518 nativeSampleRate (in)
9519 The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of
9520 `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which
9521 case a sample rate is required to convert to a size in frames.
9522
9523 performanceProfile (in)
9524 When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are
9525 zero, miniaudio will fall back to a buffer size based on the performance profile. The profile
9526 to use for this calculation is determine by this parameter.
9527
9528
9529 Return Value
9530 ------------
9531 The calculated buffer size in frames.
9532
9533
9534 Thread Safety
9535 -------------
9536 This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function
9537 should only ever be called from within the backend's device initialization routine and therefore
9538 shouldn't have any multithreading concerns.
9539
9540
9541 Callback Safety
9542 ---------------
9543 This is safe to call within the data callback, but there is no reason to ever do this.
9544
9545
9546 Remarks
9547 -------
9548 If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that
9549 is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.
9550 */
9551 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile);
9552
9553
9554
9555 /*
9556 Retrieves a friendly name for a backend.
9557 */
9558 MA_API const char* ma_get_backend_name(ma_backend backend);
9559
9560 /*
9561 Retrieves the backend enum from the given name.
9562 */
9563 MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend);
9564
9565 /*
9566 Determines whether or not the given backend is available by the compilation environment.
9567 */
9568 MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend);
9569
9570 /*
9571 Retrieves compile-time enabled backends.
9572
9573
9574 Parameters
9575 ----------
9576 pBackends (out, optional)
9577 A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting
9578 the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends.
9579
9580 backendCap (in)
9581 The capacity of the `pBackends` buffer.
9582
9583 pBackendCount (out)
9584 A pointer to the variable that will receive the enabled backend count.
9585
9586
9587 Return Value
9588 ------------
9589 MA_SUCCESS if successful.
9590 MA_INVALID_ARGS if `pBackendCount` is NULL.
9591 MA_NO_SPACE if the capacity of `pBackends` is not large enough.
9592
9593 If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values.
9594
9595
9596 Thread Safety
9597 -------------
9598 Safe.
9599
9600
9601 Callback Safety
9602 ---------------
9603 Safe.
9604
9605
9606 Remarks
9607 -------
9608 If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call
9609 this function with `pBackends` set to NULL.
9610
9611 This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null`
9612 when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at
9613 compile time with `MA_NO_NULL`.
9614
9615 The returned backends are determined based on compile time settings, not the platform it's currently running on. For
9616 example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have
9617 PulseAudio installed.
9618
9619
9620 Example 1
9621 ---------
9622 The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is
9623 given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends.
9624 Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios.
9625
9626 ```
9627 ma_backend enabledBackends[MA_BACKEND_COUNT];
9628 size_t enabledBackendCount;
9629
9630 result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount);
9631 if (result != MA_SUCCESS) {
9632 // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid.
9633 }
9634 ```
9635
9636
9637 See Also
9638 --------
9639 ma_is_backend_enabled()
9640 */
9641 MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);
9642
9643 /*
9644 Determines whether or not loopback mode is support by a backend.
9645 */
9646 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);
9647
9648 #endif /* MA_NO_DEVICE_IO */
9649
9650
9651
9652 /************************************************************************************************************************************************************
9653
9654 Utilities
9655
9656 ************************************************************************************************************************************************************/
9657
9658 /*
9659 Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
9660 */
9661 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
9662
9663 /*
9664 Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
9665 */
9666 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate);
9667
9668 /*
9669 Copies PCM frames from one buffer to another.
9670 */
9671 MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9672
9673 /*
9674 Copies silent frames into the given buffer.
9675
9676 Remarks
9677 -------
9678 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
9679 makes more sense for the purpose of mixing to initialize it to the center point.
9680 */
9681 MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9682
9683
9684 /*
9685 Offsets a pointer by the specified number of PCM frames.
9686 */
9687 MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
9688 MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
9689 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); }
9690 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); }
9691
9692
9693 /*
9694 Clips samples.
9695 */
9696 MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count);
9697 MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count);
9698 MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count);
9699 MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count);
9700 MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count);
9701 MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9702
9703 /*
9704 Helper for applying a volume factor to samples.
9705
9706 Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
9707 */
9708 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);
9709 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);
9710 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);
9711 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);
9712 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);
9713
9714 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);
9715 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);
9716 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);
9717 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);
9718 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);
9719
9720 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);
9721 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);
9722 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);
9723 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);
9724 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);
9725 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);
9726
9727 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9728 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9729 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9730 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9731 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9732 MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
9733
9734 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);
9735
9736
9737 MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume);
9738 MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume);
9739 MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume);
9740 MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume);
9741 MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume);
9742 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);
9743
9744
9745 /*
9746 Helper for converting a linear factor to gain in decibels.
9747 */
9748 MA_API float ma_volume_linear_to_db(float factor);
9749
9750 /*
9751 Helper for converting gain in decibels to a linear factor.
9752 */
9753 MA_API float ma_volume_db_to_linear(float gain);
9754
9755
9756 /*
9757 Mixes the specified number of frames in floating point format with a volume factor.
9758
9759 This will run on an optimized path when the volume is equal to 1.
9760 */
9761 MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume);
9762
9763
9764
9765
9766 /************************************************************************************************************************************************************
9767
9768 VFS
9769 ===
9770
9771 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
9772 appropriate for a given situation.
9773
9774 ************************************************************************************************************************************************************/
9775 typedef void ma_vfs;
9776 typedef ma_handle ma_vfs_file;
9777
9778 typedef enum
9779 {
9780 MA_OPEN_MODE_READ = 0x00000001,
9781 MA_OPEN_MODE_WRITE = 0x00000002
9782 } ma_open_mode_flags;
9783
9784 typedef enum
9785 {
9786 ma_seek_origin_start,
9787 ma_seek_origin_current,
9788 ma_seek_origin_end /* Not used by decoders. */
9789 } ma_seek_origin;
9790
9791 typedef struct
9792 {
9793 ma_uint64 sizeInBytes;
9794 } ma_file_info;
9795
9796 typedef struct
9797 {
9798 ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9799 ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9800 ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file);
9801 ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
9802 ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
9803 ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
9804 ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
9805 ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
9806 } ma_vfs_callbacks;
9807
9808 MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9809 MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9810 MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file);
9811 MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
9812 MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
9813 MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
9814 MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
9815 MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
9816 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);
9817
9818 typedef struct
9819 {
9820 ma_vfs_callbacks cb;
9821 ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */
9822 } ma_default_vfs;
9823
9824 MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks);
9825
9826
9827
9828 typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
9829 typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);
9830 typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);
9831
9832
9833
9834 #if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
9835 typedef enum
9836 {
9837 ma_encoding_format_unknown = 0,
9838 ma_encoding_format_wav,
9839 ma_encoding_format_flac,
9840 ma_encoding_format_mp3,
9841 ma_encoding_format_vorbis
9842 } ma_encoding_format;
9843 #endif
9844
9845 /************************************************************************************************************************************************************
9846
9847 Decoding
9848 ========
9849
9850 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
9851 you do your own synchronization.
9852
9853 ************************************************************************************************************************************************************/
9854 #ifndef MA_NO_DECODING
9855 typedef struct ma_decoder ma_decoder;
9856
9857
9858 typedef struct
9859 {
9860 ma_format preferredFormat;
9861 ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */
9862 } ma_decoding_backend_config;
9863
9864 MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount);
9865
9866
9867 typedef struct
9868 {
9869 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);
9870 ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
9871 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. */
9872 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. */
9873 void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
9874 } ma_decoding_backend_vtable;
9875
9876
9877 typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */
9878 typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);
9879 typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);
9880
9881 typedef struct
9882 {
9883 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
9884 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
9885 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
9886 ma_channel* pChannelMap;
9887 ma_channel_mix_mode channelMixMode;
9888 ma_dither_mode ditherMode;
9889 ma_resampler_config resampling;
9890 ma_allocation_callbacks allocationCallbacks;
9891 ma_encoding_format encodingFormat;
9892 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. */
9893 ma_decoding_backend_vtable** ppCustomBackendVTables;
9894 ma_uint32 customBackendCount;
9895 void* pCustomBackendUserData;
9896 } ma_decoder_config;
9897
9898 struct ma_decoder
9899 {
9900 ma_data_source_base ds;
9901 ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */
9902 const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
9903 void* pBackendUserData;
9904 ma_decoder_read_proc onRead;
9905 ma_decoder_seek_proc onSeek;
9906 ma_decoder_tell_proc onTell;
9907 void* pUserData;
9908 ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
9909 ma_format outputFormat;
9910 ma_uint32 outputChannels;
9911 ma_uint32 outputSampleRate;
9912 ma_data_converter converter; /* Data conversion is achieved by running frames through this. */
9913 void* pInputCache; /* In input format. Can be null if it's not needed. */
9914 ma_uint64 inputCacheCap; /* The capacity of the input cache. */
9915 ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
9916 ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */
9917 ma_allocation_callbacks allocationCallbacks;
9918 union
9919 {
9920 struct
9921 {
9922 ma_vfs* pVFS;
9923 ma_vfs_file file;
9924 } vfs;
9925 struct
9926 {
9927 const ma_uint8* pData;
9928 size_t dataSize;
9929 size_t currentReadPos;
9930 } memory; /* Only used for decoders that were opened against a block of memory. */
9931 } data;
9932 };
9933
9934 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
9935 MA_API ma_decoder_config ma_decoder_config_init_default(void);
9936
9937 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);
9938 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9939 MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9940 MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9941 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9942 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
9943
9944 /*
9945 Uninitializes a decoder.
9946 */
9947 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);
9948
9949 /*
9950 Reads PCM frames from the given decoder.
9951
9952 This is not thread safe without your own synchronization.
9953 */
9954 MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
9955
9956 /*
9957 Seeks to a PCM frame based on it's absolute index.
9958
9959 This is not thread safe without your own synchronization.
9960 */
9961 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
9962
9963 /*
9964 Retrieves the decoder's output data format.
9965 */
9966 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);
9967
9968 /*
9969 Retrieves the current position of the read cursor in PCM frames.
9970 */
9971 MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor);
9972
9973 /*
9974 Retrieves the length of the decoder in PCM frames.
9975
9976 Do not call this on streams of an undefined length, such as internet radio.
9977
9978 If the length is unknown or an error occurs, 0 will be returned.
9979
9980 This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
9981 uses internally.
9982
9983 For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
9984
9985 This function is not thread safe without your own synchronization.
9986 */
9987 MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength);
9988
9989 /*
9990 Retrieves the number of frames that can be read before reaching the end.
9991
9992 This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
9993 particular ensuring you do not call it on streams of an undefined length, such as internet radio.
9994
9995 If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
9996 returned.
9997 */
9998 MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);
9999
10000 /*
10001 Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
10002 pConfig should be set to what you want. On output it will be set to what you got.
10003 */
10004 MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
10005 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
10006 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
10007
10008 #endif /* MA_NO_DECODING */
10009
10010
10011 /************************************************************************************************************************************************************
10012
10013 Encoding
10014 ========
10015
10016 Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned.
10017
10018 ************************************************************************************************************************************************************/
10019 #ifndef MA_NO_ENCODING
10020 typedef struct ma_encoder ma_encoder;
10021
10022 typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten);
10023 typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin);
10024 typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder);
10025 typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
10026 typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
10027
10028 typedef struct
10029 {
10030 ma_encoding_format encodingFormat;
10031 ma_format format;
10032 ma_uint32 channels;
10033 ma_uint32 sampleRate;
10034 ma_allocation_callbacks allocationCallbacks;
10035 } ma_encoder_config;
10036
10037 MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
10038
10039 struct ma_encoder
10040 {
10041 ma_encoder_config config;
10042 ma_encoder_write_proc onWrite;
10043 ma_encoder_seek_proc onSeek;
10044 ma_encoder_init_proc onInit;
10045 ma_encoder_uninit_proc onUninit;
10046 ma_encoder_write_pcm_frames_proc onWritePCMFrames;
10047 void* pUserData;
10048 void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
10049 union
10050 {
10051 struct
10052 {
10053 ma_vfs* pVFS;
10054 ma_vfs_file file;
10055 } vfs;
10056 } data;
10057 };
10058
10059 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);
10060 MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10061 MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10062 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10063 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10064 MA_API void ma_encoder_uninit(ma_encoder* pEncoder);
10065 MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
10066
10067 #endif /* MA_NO_ENCODING */
10068
10069
10070 /************************************************************************************************************************************************************
10071
10072 Generation
10073
10074 ************************************************************************************************************************************************************/
10075 #ifndef MA_NO_GENERATION
10076 typedef enum
10077 {
10078 ma_waveform_type_sine,
10079 ma_waveform_type_square,
10080 ma_waveform_type_triangle,
10081 ma_waveform_type_sawtooth
10082 } ma_waveform_type;
10083
10084 typedef struct
10085 {
10086 ma_format format;
10087 ma_uint32 channels;
10088 ma_uint32 sampleRate;
10089 ma_waveform_type type;
10090 double amplitude;
10091 double frequency;
10092 } ma_waveform_config;
10093
10094 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);
10095
10096 typedef struct
10097 {
10098 ma_data_source_base ds;
10099 ma_waveform_config config;
10100 double advance;
10101 double time;
10102 } ma_waveform;
10103
10104 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform);
10105 MA_API void ma_waveform_uninit(ma_waveform* pWaveform);
10106 MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10107 MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex);
10108 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude);
10109 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency);
10110 MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type);
10111 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate);
10112
10113 typedef enum
10114 {
10115 ma_noise_type_white,
10116 ma_noise_type_pink,
10117 ma_noise_type_brownian
10118 } ma_noise_type;
10119
10120
10121 typedef struct
10122 {
10123 ma_format format;
10124 ma_uint32 channels;
10125 ma_noise_type type;
10126 ma_int32 seed;
10127 double amplitude;
10128 ma_bool32 duplicateChannels;
10129 } ma_noise_config;
10130
10131 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude);
10132
10133 typedef struct
10134 {
10135 ma_data_source_vtable ds;
10136 ma_noise_config config;
10137 ma_lcg lcg;
10138 union
10139 {
10140 struct
10141 {
10142 double** bin;
10143 double* accumulation;
10144 ma_uint32* counter;
10145 } pink;
10146 struct
10147 {
10148 double* accumulation;
10149 } brownian;
10150 } state;
10151
10152 /* Memory management. */
10153 void* _pHeap;
10154 ma_bool32 _ownsHeap;
10155 } ma_noise;
10156
10157 MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes);
10158 MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise);
10159 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise);
10160 MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks);
10161 MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10162 MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude);
10163 MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed);
10164 MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type);
10165
10166 #endif /* MA_NO_GENERATION */
10167
10168
10169
10170 /************************************************************************************************************************************************************
10171
10172 Resource Manager
10173
10174 ************************************************************************************************************************************************************/
10175 /* The resource manager cannot be enabled if there is no decoder. */
10176 #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING)
10177 #define MA_NO_RESOURCE_MANAGER
10178 #endif
10179
10180 #ifndef MA_NO_RESOURCE_MANAGER
10181 typedef struct ma_resource_manager ma_resource_manager;
10182 typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node;
10183 typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer;
10184 typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream;
10185 typedef struct ma_resource_manager_data_source ma_resource_manager_data_source;
10186
10187 typedef enum
10188 {
10189 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. */
10190 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. */
10191 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */
10192 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(). */
10193 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. */
10194 } ma_resource_manager_data_source_flags;
10195
10196
10197 /*
10198 Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional.
10199 */
10200 typedef struct
10201 {
10202 ma_async_notification* pNotification;
10203 ma_fence* pFence;
10204 } ma_resource_manager_pipeline_stage_notification;
10205
10206 typedef struct
10207 {
10208 ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */
10209 ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */
10210 } ma_resource_manager_pipeline_notifications;
10211
10212 MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void);
10213
10214
10215
10216 /* BEGIN BACKWARDS COMPATIBILITY */
10217 /* TODO: Remove this block in version 0.12. */
10218 #if 1
10219 #define ma_resource_manager_job ma_job
10220 #define ma_resource_manager_job_init ma_job_init
10221 #define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING
10222 #define ma_resource_manager_job_queue_config ma_job_queue_config
10223 #define ma_resource_manager_job_queue_config_init ma_job_queue_config_init
10224 #define ma_resource_manager_job_queue ma_job_queue
10225 #define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size
10226 #define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated
10227 #define ma_resource_manager_job_queue_init ma_job_queue_init
10228 #define ma_resource_manager_job_queue_uninit ma_job_queue_uninit
10229 #define ma_resource_manager_job_queue_post ma_job_queue_post
10230 #define ma_resource_manager_job_queue_next ma_job_queue_next
10231 #endif
10232 /* END BACKWARDS COMPATIBILITY */
10233
10234
10235
10236
10237 /* 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. */
10238 #ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
10239 #define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64
10240 #endif
10241
10242 typedef enum
10243 {
10244 /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */
10245 MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001,
10246
10247 /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */
10248 MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002
10249 } ma_resource_manager_flags;
10250
10251 typedef struct
10252 {
10253 const char* pFilePath;
10254 const wchar_t* pFilePathW;
10255 const ma_resource_manager_pipeline_notifications* pNotifications;
10256 ma_uint64 initialSeekPointInPCMFrames;
10257 ma_uint64 rangeBegInPCMFrames;
10258 ma_uint64 rangeEndInPCMFrames;
10259 ma_uint64 loopPointBegInPCMFrames;
10260 ma_uint64 loopPointEndInPCMFrames;
10261 ma_bool32 isLooping;
10262 ma_uint32 flags;
10263 } ma_resource_manager_data_source_config;
10264
10265 MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void);
10266
10267
10268 typedef enum
10269 {
10270 ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */
10271 ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */
10272 ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */
10273 ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */
10274 } ma_resource_manager_data_supply_type;
10275
10276 typedef struct
10277 {
10278 MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */
10279 union
10280 {
10281 struct
10282 {
10283 const void* pData;
10284 size_t sizeInBytes;
10285 } encoded;
10286 struct
10287 {
10288 const void* pData;
10289 ma_uint64 totalFrameCount;
10290 ma_uint64 decodedFrameCount;
10291 ma_format format;
10292 ma_uint32 channels;
10293 ma_uint32 sampleRate;
10294 } decoded;
10295 struct
10296 {
10297 ma_paged_audio_buffer_data data;
10298 ma_uint64 decodedFrameCount;
10299 ma_uint32 sampleRate;
10300 } decodedPaged;
10301 } backend;
10302 } ma_resource_manager_data_supply;
10303
10304 struct ma_resource_manager_data_buffer_node
10305 {
10306 ma_uint32 hashedName32; /* The hashed name. This is the key. */
10307 ma_uint32 refCount;
10308 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. */
10309 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10310 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10311 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_*()). */
10312 ma_resource_manager_data_supply data;
10313 ma_resource_manager_data_buffer_node* pParent;
10314 ma_resource_manager_data_buffer_node* pChildLo;
10315 ma_resource_manager_data_buffer_node* pChildHi;
10316 };
10317
10318 struct ma_resource_manager_data_buffer
10319 {
10320 ma_data_source_base ds; /* Base data source. A data buffer is a data source. */
10321 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */
10322 ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */
10323 ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */
10324 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10325 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10326 ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */
10327 ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */
10328 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. */
10329 MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */
10330 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. */
10331 union
10332 {
10333 ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */
10334 ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */
10335 ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */
10336 } connector; /* Connects this object to the node's data supply. */
10337 };
10338
10339 struct ma_resource_manager_data_stream
10340 {
10341 ma_data_source_base ds; /* Base data source. A data stream is a data source. */
10342 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */
10343 ma_uint32 flags; /* The flags that were passed used to initialize the stream. */
10344 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. */
10345 ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */
10346 ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */
10347 ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */
10348 MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */
10349 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. */
10350 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10351 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10352
10353 /* Written by the public API, read by the job thread. */
10354 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. */
10355
10356 /* Written by the job thread, read by the public API. */
10357 void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */
10358 MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */
10359
10360 /* Written and read by both the public API and the job thread. These must be atomic. */
10361 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. */
10362 MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */
10363 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. */
10364 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. */
10365 };
10366
10367 struct ma_resource_manager_data_source
10368 {
10369 union
10370 {
10371 ma_resource_manager_data_buffer buffer;
10372 ma_resource_manager_data_stream stream;
10373 } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */
10374
10375 ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */
10376 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10377 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10378 };
10379
10380 typedef struct
10381 {
10382 ma_allocation_callbacks allocationCallbacks;
10383 ma_log* pLog;
10384 ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */
10385 ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */
10386 ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */
10387 ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */
10388 size_t jobThreadStackSize;
10389 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. */
10390 ma_uint32 flags;
10391 ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */
10392 ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;
10393 ma_uint32 customDecodingBackendCount;
10394 void* pCustomDecodingBackendUserData;
10395 } ma_resource_manager_config;
10396
10397 MA_API ma_resource_manager_config ma_resource_manager_config_init(void);
10398
10399 struct ma_resource_manager
10400 {
10401 ma_resource_manager_config config;
10402 ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */
10403 #ifndef MA_NO_THREADING
10404 ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */
10405 ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */
10406 #endif
10407 ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */
10408 ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */
10409 ma_log log; /* Only used if no log was specified in the config. */
10410 };
10411
10412 /* Init. */
10413 MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager);
10414 MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager);
10415 MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager);
10416
10417 /* Registration. */
10418 MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags);
10419 MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags);
10420 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. */
10421 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);
10422 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. */
10423 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);
10424 MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath);
10425 MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath);
10426 MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName);
10427 MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName);
10428
10429 /* Data Buffers. */
10430 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);
10431 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);
10432 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);
10433 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);
10434 MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer);
10435 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);
10436 MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex);
10437 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);
10438 MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor);
10439 MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength);
10440 MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer);
10441 MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping);
10442 MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer);
10443 MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames);
10444
10445 /* Data Streams. */
10446 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);
10447 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);
10448 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);
10449 MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream);
10450 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);
10451 MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex);
10452 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);
10453 MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor);
10454 MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength);
10455 MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream);
10456 MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping);
10457 MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream);
10458 MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames);
10459
10460 /* Data Sources. */
10461 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);
10462 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);
10463 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);
10464 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);
10465 MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource);
10466 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);
10467 MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex);
10468 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);
10469 MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor);
10470 MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength);
10471 MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource);
10472 MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping);
10473 MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource);
10474 MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames);
10475
10476 /* Job management. */
10477 MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob);
10478 MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */
10479 MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob);
10480 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. */
10481 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. */
10482 #endif /* MA_NO_RESOURCE_MANAGER */
10483
10484
10485
10486 /************************************************************************************************************************************************************
10487
10488 Node Graph
10489
10490 ************************************************************************************************************************************************************/
10491 #ifndef MA_NO_NODE_GRAPH
10492 /* Must never exceed 254. */
10493 #ifndef MA_MAX_NODE_BUS_COUNT
10494 #define MA_MAX_NODE_BUS_COUNT 254
10495 #endif
10496
10497 /* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */
10498 #ifndef MA_MAX_NODE_LOCAL_BUS_COUNT
10499 #define MA_MAX_NODE_LOCAL_BUS_COUNT 2
10500 #endif
10501
10502 /* Use this when the bus count is determined by the node instance rather than the vtable. */
10503 #define MA_NODE_BUS_COUNT_UNKNOWN 255
10504
10505 typedef struct ma_node_graph ma_node_graph;
10506 typedef void ma_node;
10507
10508
10509 /* Node flags. */
10510 typedef enum
10511 {
10512 MA_NODE_FLAG_PASSTHROUGH = 0x00000001,
10513 MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002,
10514 MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004,
10515 MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008,
10516 MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010
10517 } ma_node_flags;
10518
10519
10520 /* The playback state of a node. Either started or stopped. */
10521 typedef enum
10522 {
10523 ma_node_state_started = 0,
10524 ma_node_state_stopped = 1
10525 } ma_node_state;
10526
10527
10528 typedef struct
10529 {
10530 /*
10531 Extended processing callback. This callback is used for effects that process input and output
10532 at different rates (i.e. they perform resampling). This is similar to the simple version, only
10533 they take two seperate frame counts: one for input, and one for output.
10534
10535 On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas
10536 `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
10537
10538 On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set
10539 `pFrameCountIn` to the number of input frames that were consumed.
10540 */
10541 void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
10542
10543 /*
10544 A callback for retrieving the number of a input frames that are required to output the
10545 specified number of output frames. You would only want to implement this when the node performs
10546 resampling. This is optional, even for nodes that perform resampling, but it does offer a
10547 small reduction in latency as it allows miniaudio to calculate the exact number of input frames
10548 to read at a time instead of having to estimate.
10549 */
10550 ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount);
10551
10552 /*
10553 The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
10554 parameters of the callbacks above.
10555 */
10556 ma_uint8 inputBusCount;
10557
10558 /*
10559 The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`
10560 parameters of the callbacks above.
10561 */
10562 ma_uint8 outputBusCount;
10563
10564 /*
10565 Flags describing characteristics of the node. This is currently just a placeholder for some
10566 ideas for later on.
10567 */
10568 ma_uint32 flags;
10569 } ma_node_vtable;
10570
10571 typedef struct
10572 {
10573 const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */
10574 ma_node_state initialState; /* Defaults to ma_node_state_started. */
10575 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). */
10576 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). */
10577 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`. */
10578 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`. */
10579 } ma_node_config;
10580
10581 MA_API ma_node_config ma_node_config_init(void);
10582
10583
10584 /*
10585 A node has multiple output buses. An output bus is attached to an input bus as an item in a linked
10586 list. Think of the input bus as a linked list, with the output bus being an item in that list.
10587 */
10588 typedef struct ma_node_output_bus ma_node_output_bus;
10589 struct ma_node_output_bus
10590 {
10591 /* Immutable. */
10592 ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */
10593 ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */
10594 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
10595
10596 /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */
10597 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. */
10598 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_*. */
10599 MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */
10600 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. */
10601 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
10602 MA_ATOMIC(4, float) volume; /* Linear. */
10603 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */
10604 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */
10605 MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */
10606 };
10607
10608 /*
10609 A node has multiple input buses. The output buses of a node are connecting to the input busses of
10610 another. An input bus is essentially just a linked list of output buses.
10611 */
10612 typedef struct ma_node_input_bus ma_node_input_bus;
10613 struct ma_node_input_bus
10614 {
10615 /* Mutable via multiple threads. */
10616 ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */
10617 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. */
10618 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
10619
10620 /* Set once at startup. */
10621 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
10622 };
10623
10624
10625 typedef struct ma_node_base ma_node_base;
10626 struct ma_node_base
10627 {
10628 /* These variables are set once at startup. */
10629 ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
10630 const ma_node_vtable* vtable;
10631 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. */
10632 ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
10633
10634 /* These variables are read and written only from the audio thread. */
10635 ma_uint16 cachedFrameCountOut;
10636 ma_uint16 cachedFrameCountIn;
10637 ma_uint16 consumedFrameCountIn;
10638
10639 /* These variables are read and written between different threads. */
10640 MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
10641 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. */
10642 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()`. */
10643 ma_uint32 inputBusCount;
10644 ma_uint32 outputBusCount;
10645 ma_node_input_bus* pInputBuses;
10646 ma_node_output_bus* pOutputBuses;
10647
10648 /* Memory management. */
10649 ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
10650 ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
10651 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. */
10652 ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
10653 };
10654
10655 MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
10656 MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
10657 MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
10658 MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10659 MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode);
10660 MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode);
10661 MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode);
10662 MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex);
10663 MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex);
10664 MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex);
10665 MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex);
10666 MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode);
10667 MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume);
10668 MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex);
10669 MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state);
10670 MA_API ma_node_state ma_node_get_state(const ma_node* pNode);
10671 MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime);
10672 MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state);
10673 MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime);
10674 MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd);
10675 MA_API ma_uint64 ma_node_get_time(const ma_node* pNode);
10676 MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);
10677
10678
10679 typedef struct
10680 {
10681 ma_uint32 channels;
10682 ma_uint16 nodeCacheCapInFrames;
10683 } ma_node_graph_config;
10684
10685 MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels);
10686
10687
10688 struct ma_node_graph
10689 {
10690 /* Immutable. */
10691 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. */
10692 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(). */
10693 ma_uint16 nodeCacheCapInFrames;
10694
10695 /* Read and written by multiple threads. */
10696 MA_ATOMIC(4, ma_bool32) isReading;
10697 };
10698
10699 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
10700 MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks);
10701 MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph);
10702 MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10703 MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph);
10704 MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph);
10705 MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime);
10706
10707
10708
10709 /* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */
10710 typedef struct
10711 {
10712 ma_node_config nodeConfig;
10713 ma_data_source* pDataSource;
10714 } ma_data_source_node_config;
10715
10716 MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource);
10717
10718
10719 typedef struct
10720 {
10721 ma_node_base base;
10722 ma_data_source* pDataSource;
10723 } ma_data_source_node;
10724
10725 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);
10726 MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks);
10727 MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping);
10728 MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode);
10729
10730
10731 /* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */
10732 typedef struct
10733 {
10734 ma_node_config nodeConfig;
10735 ma_uint32 channels;
10736 ma_uint32 outputBusCount;
10737 } ma_splitter_node_config;
10738
10739 MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels);
10740
10741
10742 typedef struct
10743 {
10744 ma_node_base base;
10745 } ma_splitter_node;
10746
10747 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);
10748 MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks);
10749
10750
10751 /*
10752 Biquad Node
10753 */
10754 typedef struct
10755 {
10756 ma_node_config nodeConfig;
10757 ma_biquad_config biquad;
10758 } ma_biquad_node_config;
10759
10760 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);
10761
10762
10763 typedef struct
10764 {
10765 ma_node_base baseNode;
10766 ma_biquad biquad;
10767 } ma_biquad_node;
10768
10769 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);
10770 MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode);
10771 MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10772
10773
10774 /*
10775 Low Pass Filter Node
10776 */
10777 typedef struct
10778 {
10779 ma_node_config nodeConfig;
10780 ma_lpf_config lpf;
10781 } ma_lpf_node_config;
10782
10783 MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10784
10785
10786 typedef struct
10787 {
10788 ma_node_base baseNode;
10789 ma_lpf lpf;
10790 } ma_lpf_node;
10791
10792 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);
10793 MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode);
10794 MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10795
10796
10797 /*
10798 High Pass Filter Node
10799 */
10800 typedef struct
10801 {
10802 ma_node_config nodeConfig;
10803 ma_hpf_config hpf;
10804 } ma_hpf_node_config;
10805
10806 MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10807
10808
10809 typedef struct
10810 {
10811 ma_node_base baseNode;
10812 ma_hpf hpf;
10813 } ma_hpf_node;
10814
10815 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);
10816 MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode);
10817 MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10818
10819
10820 /*
10821 Band Pass Filter Node
10822 */
10823 typedef struct
10824 {
10825 ma_node_config nodeConfig;
10826 ma_bpf_config bpf;
10827 } ma_bpf_node_config;
10828
10829 MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10830
10831
10832 typedef struct
10833 {
10834 ma_node_base baseNode;
10835 ma_bpf bpf;
10836 } ma_bpf_node;
10837
10838 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);
10839 MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode);
10840 MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10841
10842
10843 /*
10844 Notching Filter Node
10845 */
10846 typedef struct
10847 {
10848 ma_node_config nodeConfig;
10849 ma_notch_config notch;
10850 } ma_notch_node_config;
10851
10852 MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
10853
10854
10855 typedef struct
10856 {
10857 ma_node_base baseNode;
10858 ma_notch2 notch;
10859 } ma_notch_node;
10860
10861 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);
10862 MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode);
10863 MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10864
10865
10866 /*
10867 Peaking Filter Node
10868 */
10869 typedef struct
10870 {
10871 ma_node_config nodeConfig;
10872 ma_peak_config peak;
10873 } ma_peak_node_config;
10874
10875 MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
10876
10877
10878 typedef struct
10879 {
10880 ma_node_base baseNode;
10881 ma_peak2 peak;
10882 } ma_peak_node;
10883
10884 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);
10885 MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode);
10886 MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10887
10888
10889 /*
10890 Low Shelf Filter Node
10891 */
10892 typedef struct
10893 {
10894 ma_node_config nodeConfig;
10895 ma_loshelf_config loshelf;
10896 } ma_loshelf_node_config;
10897
10898 MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
10899
10900
10901 typedef struct
10902 {
10903 ma_node_base baseNode;
10904 ma_loshelf2 loshelf;
10905 } ma_loshelf_node;
10906
10907 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);
10908 MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode);
10909 MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10910
10911
10912 /*
10913 High Shelf Filter Node
10914 */
10915 typedef struct
10916 {
10917 ma_node_config nodeConfig;
10918 ma_hishelf_config hishelf;
10919 } ma_hishelf_node_config;
10920
10921 MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
10922
10923
10924 typedef struct
10925 {
10926 ma_node_base baseNode;
10927 ma_hishelf2 hishelf;
10928 } ma_hishelf_node;
10929
10930 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);
10931 MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode);
10932 MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10933
10934
10935 typedef struct
10936 {
10937 ma_node_config nodeConfig;
10938 ma_delay_config delay;
10939 } ma_delay_node_config;
10940
10941 MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
10942
10943
10944 typedef struct
10945 {
10946 ma_node_base baseNode;
10947 ma_delay delay;
10948 } ma_delay_node;
10949
10950 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);
10951 MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks);
10952 MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value);
10953 MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode);
10954 MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value);
10955 MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode);
10956 MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value);
10957 MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode);
10958 #endif /* MA_NO_NODE_GRAPH */
10959
10960
10961 /* SECTION: miniaudio_engine.h */
10962 /************************************************************************************************************************************************************
10963
10964 Engine
10965
10966 ************************************************************************************************************************************************************/
10967 #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
10968 typedef struct ma_engine ma_engine;
10969 typedef struct ma_sound ma_sound;
10970
10971
10972 /* Sound flags. */
10973 typedef enum
10974 {
10975 /* Resource manager flags. */
10976 MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */
10977 MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */
10978 MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
10979 MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
10980 MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */
10981
10982 /* ma_sound specific flags. */
10983 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. */
10984 MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
10985 MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */
10986 } ma_sound_flags;
10987
10988 #ifndef MA_ENGINE_MAX_LISTENERS
10989 #define MA_ENGINE_MAX_LISTENERS 4
10990 #endif
10991
10992 #define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1)
10993
10994 typedef enum
10995 {
10996 ma_engine_node_type_sound,
10997 ma_engine_node_type_group
10998 } ma_engine_node_type;
10999
11000 typedef struct
11001 {
11002 ma_engine* pEngine;
11003 ma_engine_node_type type;
11004 ma_uint32 channelsIn;
11005 ma_uint32 channelsOut;
11006 ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */
11007 ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */
11008 ma_mono_expansion_mode monoExpansionMode;
11009 ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */
11010 ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */
11011 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. */
11012 } ma_engine_node_config;
11013
11014 MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags);
11015
11016
11017 /* Base node object for both ma_sound and ma_sound_group. */
11018 typedef struct
11019 {
11020 ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */
11021 ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */
11022 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. */
11023 ma_uint32 volumeSmoothTimeInPCMFrames;
11024 ma_mono_expansion_mode monoExpansionMode;
11025 ma_fader fader;
11026 ma_linear_resampler resampler; /* For pitch shift. */
11027 ma_spatializer spatializer;
11028 ma_panner panner;
11029 ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */
11030 ma_atomic_float volume; /* Defaults to 1. */
11031 MA_ATOMIC(4, float) pitch;
11032 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. */
11033 float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */
11034 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. */
11035 MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */
11036 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. */
11037
11038 /* Memory management. */
11039 ma_bool8 _ownsHeap;
11040 void* _pHeap;
11041 } ma_engine_node;
11042
11043 MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes);
11044 MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode);
11045 MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode);
11046 MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks);
11047
11048
11049 #define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF
11050
11051 /* Callback for when a sound reaches the end. */
11052 typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound);
11053
11054 typedef struct
11055 {
11056 const char* pFilePath; /* Set this to load from the resource manager. */
11057 const wchar_t* pFilePathW; /* Set this to load from the resource manager. */
11058 ma_data_source* pDataSource; /* Set this to load from an existing data source. */
11059 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`. */
11060 ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */
11061 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. */
11062 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). */
11063 ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
11064 ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */
11065 ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */
11066 ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */
11067 ma_uint64 rangeBegInPCMFrames;
11068 ma_uint64 rangeEndInPCMFrames;
11069 ma_uint64 loopPointBegInPCMFrames;
11070 ma_uint64 loopPointEndInPCMFrames;
11071 ma_bool32 isLooping;
11072 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_*(). */
11073 void* pEndCallbackUserData;
11074 #ifndef MA_NO_RESOURCE_MANAGER
11075 ma_resource_manager_pipeline_notifications initNotifications;
11076 #endif
11077 ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */
11078 } ma_sound_config;
11079
11080 MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */
11081 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. */
11082
11083 struct ma_sound
11084 {
11085 ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */
11086 ma_data_source* pDataSource;
11087 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. */
11088 MA_ATOMIC(4, ma_bool32) atEnd;
11089 ma_sound_end_proc endCallback;
11090 void* pEndCallbackUserData;
11091 ma_bool8 ownsDataSource;
11092
11093 /*
11094 We're declaring a resource manager data source object here to save us a malloc when loading a
11095 sound via the resource manager, which I *think* will be the most common scenario.
11096 */
11097 #ifndef MA_NO_RESOURCE_MANAGER
11098 ma_resource_manager_data_source* pResourceManagerDataSource;
11099 #endif
11100 };
11101
11102 /* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */
11103 typedef struct ma_sound_inlined ma_sound_inlined;
11104 struct ma_sound_inlined
11105 {
11106 ma_sound sound;
11107 ma_sound_inlined* pNext;
11108 ma_sound_inlined* pPrev;
11109 };
11110
11111 /* A sound group is just a sound. */
11112 typedef ma_sound_config ma_sound_group_config;
11113 typedef ma_sound ma_sound_group;
11114
11115 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. */
11116 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. */
11117
11118 typedef struct
11119 {
11120 #if !defined(MA_NO_RESOURCE_MANAGER)
11121 ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */
11122 #endif
11123 #if !defined(MA_NO_DEVICE_IO)
11124 ma_context* pContext;
11125 ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */
11126 ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */
11127 ma_device_notification_proc notificationCallback;
11128 #endif
11129 ma_log* pLog; /* When set to NULL, will use the context's log. */
11130 ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
11131 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. */
11132 ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */
11133 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.*/
11134 ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */
11135 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
11136 ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
11137 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). */
11138 ma_allocation_callbacks allocationCallbacks;
11139 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(). */
11140 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. */
11141 ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
11142 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. */
11143 } ma_engine_config;
11144
11145 MA_API ma_engine_config ma_engine_config_init(void);
11146
11147
11148 struct ma_engine
11149 {
11150 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. */
11151 #if !defined(MA_NO_RESOURCE_MANAGER)
11152 ma_resource_manager* pResourceManager;
11153 #endif
11154 #if !defined(MA_NO_DEVICE_IO)
11155 ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
11156 #endif
11157 ma_log* pLog;
11158 ma_uint32 sampleRate;
11159 ma_uint32 listenerCount;
11160 ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS];
11161 ma_allocation_callbacks allocationCallbacks;
11162 ma_bool8 ownsResourceManager;
11163 ma_bool8 ownsDevice;
11164 ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */
11165 ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */
11166 MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */
11167 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */
11168 ma_uint32 defaultVolumeSmoothTimeInPCMFrames;
11169 ma_mono_expansion_mode monoExpansionMode;
11170 };
11171
11172 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine);
11173 MA_API void ma_engine_uninit(ma_engine* pEngine);
11174 MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
11175 MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine);
11176 #if !defined(MA_NO_RESOURCE_MANAGER)
11177 MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine);
11178 #endif
11179 MA_API ma_device* ma_engine_get_device(ma_engine* pEngine);
11180 MA_API ma_log* ma_engine_get_log(ma_engine* pEngine);
11181 MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine);
11182 MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine);
11183 MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine);
11184 MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime);
11185 MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime);
11186 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. */
11187 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. */
11188 MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine);
11189 MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine);
11190
11191 MA_API ma_result ma_engine_start(ma_engine* pEngine);
11192 MA_API ma_result ma_engine_stop(ma_engine* pEngine);
11193 MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume);
11194 MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB);
11195
11196 MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine);
11197 MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ);
11198 MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11199 MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex);
11200 MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11201 MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex);
11202 MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11203 MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex);
11204 MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
11205 MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
11206 MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11207 MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex);
11208 MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled);
11209 MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex);
11210
11211 #ifndef MA_NO_RESOURCE_MANAGER
11212 MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex);
11213 MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */
11214 #endif
11215
11216 #ifndef MA_NO_RESOURCE_MANAGER
11217 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);
11218 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);
11219 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);
11220 #endif
11221 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);
11222 MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound);
11223 MA_API void ma_sound_uninit(ma_sound* pSound);
11224 MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound);
11225 MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound);
11226 MA_API ma_result ma_sound_start(ma_sound* pSound);
11227 MA_API ma_result ma_sound_stop(ma_sound* pSound);
11228 MA_API void ma_sound_set_volume(ma_sound* pSound, float volume);
11229 MA_API float ma_sound_get_volume(const ma_sound* pSound);
11230 MA_API void ma_sound_set_pan(ma_sound* pSound, float pan);
11231 MA_API float ma_sound_get_pan(const ma_sound* pSound);
11232 MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode);
11233 MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound);
11234 MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch);
11235 MA_API float ma_sound_get_pitch(const ma_sound* pSound);
11236 MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled);
11237 MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound);
11238 MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex);
11239 MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound);
11240 MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound);
11241 MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound);
11242 MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z);
11243 MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound);
11244 MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z);
11245 MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound);
11246 MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z);
11247 MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound);
11248 MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel);
11249 MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound);
11250 MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning);
11251 MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound);
11252 MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff);
11253 MA_API float ma_sound_get_rolloff(const ma_sound* pSound);
11254 MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain);
11255 MA_API float ma_sound_get_min_gain(const ma_sound* pSound);
11256 MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain);
11257 MA_API float ma_sound_get_max_gain(const ma_sound* pSound);
11258 MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance);
11259 MA_API float ma_sound_get_min_distance(const ma_sound* pSound);
11260 MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance);
11261 MA_API float ma_sound_get_max_distance(const ma_sound* pSound);
11262 MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
11263 MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
11264 MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor);
11265 MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound);
11266 MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor);
11267 MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound);
11268 MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
11269 MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
11270 MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound);
11271 MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
11272 MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
11273 MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
11274 MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
11275 MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound);
11276 MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound);
11277 MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping);
11278 MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound);
11279 MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound);
11280 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(). */
11281 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);
11282 MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor);
11283 MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength);
11284 MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor);
11285 MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength);
11286 MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData);
11287
11288 MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup);
11289 MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup);
11290 MA_API void ma_sound_group_uninit(ma_sound_group* pGroup);
11291 MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup);
11292 MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup);
11293 MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup);
11294 MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume);
11295 MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup);
11296 MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan);
11297 MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup);
11298 MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode);
11299 MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup);
11300 MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch);
11301 MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup);
11302 MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled);
11303 MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup);
11304 MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex);
11305 MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup);
11306 MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup);
11307 MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup);
11308 MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z);
11309 MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup);
11310 MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z);
11311 MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup);
11312 MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z);
11313 MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup);
11314 MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel);
11315 MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup);
11316 MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning);
11317 MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup);
11318 MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff);
11319 MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup);
11320 MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain);
11321 MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup);
11322 MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain);
11323 MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup);
11324 MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance);
11325 MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup);
11326 MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance);
11327 MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup);
11328 MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
11329 MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
11330 MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor);
11331 MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup);
11332 MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor);
11333 MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup);
11334 MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
11335 MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
11336 MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup);
11337 MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
11338 MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
11339 MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
11340 MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
11341 MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup);
11342 MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup);
11343 #endif /* MA_NO_ENGINE */
11344 /* END SECTION: miniaudio_engine.h */
11345
11346 #ifdef __cplusplus
11347 }
11348 #endif
11349 #endif /* miniaudio_h */
11350
11351
11352 /*
11353 This is for preventing greying out of the implementation section.
11354 */
11355 #if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__)
11356 #define MINIAUDIO_IMPLEMENTATION
11357 #endif
11358
11359 /************************************************************************************************************************************************************
11360 *************************************************************************************************************************************************************
11361
11362 IMPLEMENTATION
11363
11364 *************************************************************************************************************************************************************
11365 ************************************************************************************************************************************************************/
11366 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
11367 #ifndef miniaudio_c
11368 #define miniaudio_c
11369
11370 #include <assert.h>
11371 #include <limits.h> /* For INT_MAX */
11372 #include <math.h> /* sin(), etc. */
11373 #include <stdlib.h> /* For malloc(), free(), wcstombs(). */
11374 #include <string.h> /* For memset() */
11375
11376 #include <stdarg.h>
11377 #include <stdio.h>
11378 #if !defined(_MSC_VER) && !defined(__DMC__)
11379 #include <strings.h> /* For strcasecmp(). */
11380 #include <wchar.h> /* For wcslen(), wcsrtombs() */
11381 #endif
11382 #ifdef _MSC_VER
11383 #include <float.h> /* For _controlfp_s constants */
11384 #endif
11385
11386 #if defined(MA_WIN32)
11387 #include <windows.h>
11388
11389 /*
11390 There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols
11391 such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're
11392 unavailable.
11393 */
11394 #ifndef STGM_READ
11395 #define STGM_READ 0x00000000L
11396 #endif
11397 #ifndef CLSCTX_ALL
11398 #define CLSCTX_ALL 23
11399 #endif
11400
11401 /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */
11402 typedef struct ma_IUnknown ma_IUnknown;
11403 #endif
11404
11405 #if !defined(MA_WIN32)
11406 #include <sched.h>
11407 #include <sys/time.h> /* select() (used for ma_sleep()). */
11408 #include <pthread.h>
11409 #endif
11410
11411 #ifdef MA_NX
11412 #include <time.h> /* For nanosleep() */
11413 #endif
11414
11415 #include <sys/stat.h> /* For fstat(), etc. */
11416
11417 #ifdef MA_EMSCRIPTEN
11418 #include <emscripten/emscripten.h>
11419 #endif
11420
11421
11422 #if !defined(MA_64BIT) && !defined(MA_32BIT)
11423 #ifdef _WIN32
11424 #ifdef _WIN64
11425 #define MA_64BIT
11426 #else
11427 #define MA_32BIT
11428 #endif
11429 #endif
11430 #endif
11431
11432 #if !defined(MA_64BIT) && !defined(MA_32BIT)
11433 #ifdef __GNUC__
11434 #ifdef __LP64__
11435 #define MA_64BIT
11436 #else
11437 #define MA_32BIT
11438 #endif
11439 #endif
11440 #endif
11441
11442 #if !defined(MA_64BIT) && !defined(MA_32BIT)
11443 #include <stdint.h>
11444 #if INTPTR_MAX == INT64_MAX
11445 #define MA_64BIT
11446 #else
11447 #define MA_32BIT
11448 #endif
11449 #endif
11450
11451 /* Architecture Detection */
11452 #if defined(__x86_64__) || defined(_M_X64)
11453 #define MA_X64
11454 #elif defined(__i386) || defined(_M_IX86)
11455 #define MA_X86
11456 #elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
11457 #define MA_ARM
11458 #endif
11459
11460 /* Intrinsics Support */
11461 #if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__)
11462 #if defined(_MSC_VER) && !defined(__clang__)
11463 /* MSVC. */
11464 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
11465 #define MA_SUPPORT_SSE2
11466 #endif
11467 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
11468 /* #define MA_SUPPORT_AVX*/
11469 /*#endif*/
11470 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
11471 #define MA_SUPPORT_AVX2
11472 #endif
11473 #else
11474 /* Assume GNUC-style. */
11475 #if defined(__SSE2__) && !defined(MA_NO_SSE2)
11476 #define MA_SUPPORT_SSE2
11477 #endif
11478 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
11479 /* #define MA_SUPPORT_AVX*/
11480 /*#endif*/
11481 #if defined(__AVX2__) && !defined(MA_NO_AVX2)
11482 #define MA_SUPPORT_AVX2
11483 #endif
11484 #endif
11485
11486 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
11487 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
11488 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
11489 #define MA_SUPPORT_SSE2
11490 #endif
11491 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
11492 /* #define MA_SUPPORT_AVX*/
11493 /*#endif*/
11494 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
11495 #define MA_SUPPORT_AVX2
11496 #endif
11497 #endif
11498
11499 #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
11500 #include <immintrin.h>
11501 #elif defined(MA_SUPPORT_SSE2)
11502 #include <emmintrin.h>
11503 #endif
11504 #endif
11505
11506 #if defined(MA_ARM)
11507 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
11508 #define MA_SUPPORT_NEON
11509 #include <arm_neon.h>
11510 #endif
11511 #endif
11512
11513 /* Begin globally disabled warnings. */
11514 #if defined(_MSC_VER)
11515 #pragma warning(push)
11516 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
11517 #pragma warning(disable:4049) /* compiler limit : terminating line number emission */
11518 #endif
11519
11520 #if defined(MA_X64) || defined(MA_X86)
11521 #if defined(_MSC_VER) && !defined(__clang__)
11522 #if _MSC_VER >= 1400
11523 #include <intrin.h>
11524 static MA_INLINE void ma_cpuid(int info[4], int fid)
11525 {
11526 __cpuid(info, fid);
11527 }
11528 #else
11529 #define MA_NO_CPUID
11530 #endif
11531
11532 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
11533 static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
11534 {
11535 return _xgetbv(reg);
11536 }
11537 #else
11538 #define MA_NO_XGETBV
11539 #endif
11540 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
11541 static MA_INLINE void ma_cpuid(int info[4], int fid)
11542 {
11543 /*
11544 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
11545 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
11546 supporting different assembly dialects.
11547
11548 What's basically happening is that we're saving and restoring the ebx register manually.
11549 */
11550 #if defined(DRFLAC_X86) && defined(__PIC__)
11551 __asm__ __volatile__ (
11552 "xchg{l} {%%}ebx, %k1;"
11553 "cpuid;"
11554 "xchg{l} {%%}ebx, %k1;"
11555 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
11556 );
11557 #else
11558 __asm__ __volatile__ (
11559 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
11560 );
11561 #endif
11562 }
11563
11564 static MA_INLINE ma_uint64 ma_xgetbv(int reg)
11565 {
11566 unsigned int hi;
11567 unsigned int lo;
11568
11569 __asm__ __volatile__ (
11570 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
11571 );
11572
11573 return ((ma_uint64)hi << 32) | (ma_uint64)lo;
11574 }
11575 #else
11576 #define MA_NO_CPUID
11577 #define MA_NO_XGETBV
11578 #endif
11579 #else
11580 #define MA_NO_CPUID
11581 #define MA_NO_XGETBV
11582 #endif
11583
11584 static MA_INLINE ma_bool32 ma_has_sse2(void)
11585 {
11586 #if defined(MA_SUPPORT_SSE2)
11587 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
11588 #if defined(MA_X64)
11589 return MA_TRUE; /* 64-bit targets always support SSE2. */
11590 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
11591 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
11592 #else
11593 #if defined(MA_NO_CPUID)
11594 return MA_FALSE;
11595 #else
11596 int info[4];
11597 ma_cpuid(info, 1);
11598 return (info[3] & (1 << 26)) != 0;
11599 #endif
11600 #endif
11601 #else
11602 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
11603 #endif
11604 #else
11605 return MA_FALSE; /* No compiler support. */
11606 #endif
11607 }
11608
11609 #if 0
11610 static MA_INLINE ma_bool32 ma_has_avx()
11611 {
11612 #if defined(MA_SUPPORT_AVX)
11613 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
11614 #if defined(_AVX_) || defined(__AVX__)
11615 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
11616 #else
11617 /* AVX requires both CPU and OS support. */
11618 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
11619 return MA_FALSE;
11620 #else
11621 int info[4];
11622 ma_cpuid(info, 1);
11623 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
11624 ma_uint64 xrc = ma_xgetbv(0);
11625 if ((xrc & 0x06) == 0x06) {
11626 return MA_TRUE;
11627 } else {
11628 return MA_FALSE;
11629 }
11630 } else {
11631 return MA_FALSE;
11632 }
11633 #endif
11634 #endif
11635 #else
11636 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
11637 #endif
11638 #else
11639 return MA_FALSE; /* No compiler support. */
11640 #endif
11641 }
11642 #endif
11643
11644 static MA_INLINE ma_bool32 ma_has_avx2(void)
11645 {
11646 #if defined(MA_SUPPORT_AVX2)
11647 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
11648 #if defined(_AVX2_) || defined(__AVX2__)
11649 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
11650 #else
11651 /* AVX2 requires both CPU and OS support. */
11652 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
11653 return MA_FALSE;
11654 #else
11655 int info1[4];
11656 int info7[4];
11657 ma_cpuid(info1, 1);
11658 ma_cpuid(info7, 7);
11659 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
11660 ma_uint64 xrc = ma_xgetbv(0);
11661 if ((xrc & 0x06) == 0x06) {
11662 return MA_TRUE;
11663 } else {
11664 return MA_FALSE;
11665 }
11666 } else {
11667 return MA_FALSE;
11668 }
11669 #endif
11670 #endif
11671 #else
11672 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
11673 #endif
11674 #else
11675 return MA_FALSE; /* No compiler support. */
11676 #endif
11677 }
11678
11679 static MA_INLINE ma_bool32 ma_has_neon(void)
11680 {
11681 #if defined(MA_SUPPORT_NEON)
11682 #if defined(MA_ARM) && !defined(MA_NO_NEON)
11683 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
11684 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
11685 #else
11686 /* TODO: Runtime check. */
11687 return MA_FALSE;
11688 #endif
11689 #else
11690 return MA_FALSE; /* NEON is only supported on ARM architectures. */
11691 #endif
11692 #else
11693 return MA_FALSE; /* No compiler support. */
11694 #endif
11695 }
11696
11697 #if defined(__has_builtin)
11698 #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
11699 #else
11700 #define MA_COMPILER_HAS_BUILTIN(x) 0
11701 #endif
11702
11703 #ifndef MA_ASSUME
11704 #if MA_COMPILER_HAS_BUILTIN(__builtin_assume)
11705 #define MA_ASSUME(x) __builtin_assume(x)
11706 #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable)
11707 #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
11708 #elif defined(_MSC_VER)
11709 #define MA_ASSUME(x) __assume(x)
11710 #else
11711 #define MA_ASSUME(x) (void)(x)
11712 #endif
11713 #endif
11714
11715 #ifndef MA_RESTRICT
11716 #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER)
11717 #define MA_RESTRICT __restrict
11718 #else
11719 #define MA_RESTRICT
11720 #endif
11721 #endif
11722
11723 #if defined(_MSC_VER) && _MSC_VER >= 1400
11724 #define MA_HAS_BYTESWAP16_INTRINSIC
11725 #define MA_HAS_BYTESWAP32_INTRINSIC
11726 #define MA_HAS_BYTESWAP64_INTRINSIC
11727 #elif defined(__clang__)
11728 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16)
11729 #define MA_HAS_BYTESWAP16_INTRINSIC
11730 #endif
11731 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32)
11732 #define MA_HAS_BYTESWAP32_INTRINSIC
11733 #endif
11734 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64)
11735 #define MA_HAS_BYTESWAP64_INTRINSIC
11736 #endif
11737 #elif defined(__GNUC__)
11738 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
11739 #define MA_HAS_BYTESWAP32_INTRINSIC
11740 #define MA_HAS_BYTESWAP64_INTRINSIC
11741 #endif
11742 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
11743 #define MA_HAS_BYTESWAP16_INTRINSIC
11744 #endif
11745 #endif
11746
11747
11748 static MA_INLINE ma_bool32 ma_is_little_endian(void)
11749 {
11750 #if defined(MA_X86) || defined(MA_X64)
11751 return MA_TRUE;
11752 #else
11753 int n = 1;
11754 return (*(char*)&n) == 1;
11755 #endif
11756 }
11757
11758 static MA_INLINE ma_bool32 ma_is_big_endian(void)
11759 {
11760 return !ma_is_little_endian();
11761 }
11762
11763
11764 static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)
11765 {
11766 #ifdef MA_HAS_BYTESWAP32_INTRINSIC
11767 #if defined(_MSC_VER)
11768 return _byteswap_ulong(n);
11769 #elif defined(__GNUC__) || defined(__clang__)
11770 #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. */
11771 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
11772 ma_uint32 r;
11773 __asm__ __volatile__ (
11774 #if defined(MA_64BIT)
11775 "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! */
11776 #else
11777 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
11778 #endif
11779 );
11780 return r;
11781 #else
11782 return __builtin_bswap32(n);
11783 #endif
11784 #else
11785 #error "This compiler does not support the byte swap intrinsic."
11786 #endif
11787 #else
11788 return ((n & 0xFF000000) >> 24) |
11789 ((n & 0x00FF0000) >> 8) |
11790 ((n & 0x0000FF00) << 8) |
11791 ((n & 0x000000FF) << 24);
11792 #endif
11793 }
11794
11795
11796 #if !defined(MA_EMSCRIPTEN)
11797 #ifdef MA_WIN32
11798 static void ma_sleep__win32(ma_uint32 milliseconds)
11799 {
11800 Sleep((DWORD)milliseconds);
11801 }
11802 #endif
11803 #ifdef MA_POSIX
11804 static void ma_sleep__posix(ma_uint32 milliseconds)
11805 {
11806 #ifdef MA_EMSCRIPTEN
11807 (void)milliseconds;
11808 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
11809 #else
11810 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX)
11811 struct timespec ts;
11812 ts.tv_sec = milliseconds / 1000;
11813 ts.tv_nsec = milliseconds % 1000 * 1000000;
11814 nanosleep(&ts, NULL);
11815 #else
11816 struct timeval tv;
11817 tv.tv_sec = milliseconds / 1000;
11818 tv.tv_usec = milliseconds % 1000 * 1000;
11819 select(0, NULL, NULL, NULL, &tv);
11820 #endif
11821 #endif
11822 }
11823 #endif
11824
11825 static MA_INLINE void ma_sleep(ma_uint32 milliseconds)
11826 {
11827 #ifdef MA_WIN32
11828 ma_sleep__win32(milliseconds);
11829 #endif
11830 #ifdef MA_POSIX
11831 ma_sleep__posix(milliseconds);
11832 #endif
11833 }
11834 #endif
11835
11836 static MA_INLINE void ma_yield(void)
11837 {
11838 #if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
11839 /* x86/x64 */
11840 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__)
11841 #if _MSC_VER >= 1400
11842 _mm_pause();
11843 #else
11844 #if defined(__DMC__)
11845 /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
11846 __asm nop;
11847 #else
11848 __asm pause;
11849 #endif
11850 #endif
11851 #else
11852 __asm__ __volatile__ ("pause");
11853 #endif
11854 #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__)
11855 /* ARM */
11856 #if defined(_MSC_VER)
11857 /* 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. */
11858 __yield();
11859 #else
11860 __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */
11861 #endif
11862 #else
11863 /* Unknown or unsupported architecture. No-op. */
11864 #endif
11865 }
11866
11867
11868 #define MA_MM_DENORMALS_ZERO_MASK 0x0040
11869 #define MA_MM_FLUSH_ZERO_MASK 0x8000
11870
11871 static MA_INLINE unsigned int ma_disable_denormals(void)
11872 {
11873 unsigned int prevState;
11874
11875 #if defined(_MSC_VER)
11876 {
11877 /*
11878 Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't
11879 know which version of Visual Studio first added support for _controlfp_s(), but I do know
11880 that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older
11881 versions of Visual Studio, let me know and I'll make the necessary adjustment.
11882 */
11883 #if _MSC_VER <= 1200
11884 {
11885 prevState = _statusfp();
11886 _controlfp(prevState | _DN_FLUSH, _MCW_DN);
11887 }
11888 #else
11889 {
11890 unsigned int unused;
11891 _controlfp_s(&prevState, 0, 0);
11892 _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN);
11893 }
11894 #endif
11895 }
11896 #elif defined(MA_X86) || defined(MA_X64)
11897 {
11898 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */
11899 {
11900 prevState = _mm_getcsr();
11901 _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK);
11902 }
11903 #else
11904 {
11905 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */
11906 prevState = 0;
11907 }
11908 #endif
11909 }
11910 #else
11911 {
11912 /* Unknown or unsupported architecture. No-op. */
11913 prevState = 0;
11914 }
11915 #endif
11916
11917 return prevState;
11918 }
11919
11920 static MA_INLINE void ma_restore_denormals(unsigned int prevState)
11921 {
11922 #if defined(_MSC_VER)
11923 {
11924 /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */
11925 #if _MSC_VER <= 1200
11926 {
11927 _controlfp(prevState, _MCW_DN);
11928 }
11929 #else
11930 {
11931 unsigned int unused;
11932 _controlfp_s(&unused, prevState, _MCW_DN);
11933 }
11934 #endif
11935 }
11936 #elif defined(MA_X86) || defined(MA_X64)
11937 {
11938 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */
11939 {
11940 _mm_setcsr(prevState);
11941 }
11942 #else
11943 {
11944 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */
11945 (void)prevState;
11946 }
11947 #endif
11948 }
11949 #else
11950 {
11951 /* Unknown or unsupported architecture. No-op. */
11952 (void)prevState;
11953 }
11954 #endif
11955 }
11956
11957
11958 #ifdef MA_ANDROID
11959 #include <sys/system_properties.h>
11960
11961 int ma_android_sdk_version()
11962 {
11963 char sdkVersion[PROP_VALUE_MAX + 1] = {0, };
11964 if (__system_property_get("ro.build.version.sdk", sdkVersion)) {
11965 return atoi(sdkVersion);
11966 }
11967
11968 return 0;
11969 }
11970 #endif
11971
11972
11973 #ifndef MA_COINIT_VALUE
11974 #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */
11975 #endif
11976
11977
11978 #ifndef MA_FLT_MAX
11979 #ifdef FLT_MAX
11980 #define MA_FLT_MAX FLT_MAX
11981 #else
11982 #define MA_FLT_MAX 3.402823466e+38F
11983 #endif
11984 #endif
11985
11986
11987 #ifndef MA_PI
11988 #define MA_PI 3.14159265358979323846264f
11989 #endif
11990 #ifndef MA_PI_D
11991 #define MA_PI_D 3.14159265358979323846264
11992 #endif
11993 #ifndef MA_TAU
11994 #define MA_TAU 6.28318530717958647693f
11995 #endif
11996 #ifndef MA_TAU_D
11997 #define MA_TAU_D 6.28318530717958647693
11998 #endif
11999
12000
12001 /* The default format when ma_format_unknown (0) is requested when initializing a device. */
12002 #ifndef MA_DEFAULT_FORMAT
12003 #define MA_DEFAULT_FORMAT ma_format_f32
12004 #endif
12005
12006 /* The default channel count to use when 0 is used when initializing a device. */
12007 #ifndef MA_DEFAULT_CHANNELS
12008 #define MA_DEFAULT_CHANNELS 2
12009 #endif
12010
12011 /* The default sample rate to use when 0 is used when initializing a device. */
12012 #ifndef MA_DEFAULT_SAMPLE_RATE
12013 #define MA_DEFAULT_SAMPLE_RATE 48000
12014 #endif
12015
12016 /* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
12017 #ifndef MA_DEFAULT_PERIODS
12018 #define MA_DEFAULT_PERIODS 3
12019 #endif
12020
12021 /* The default period size in milliseconds for low latency mode. */
12022 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
12023 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
12024 #endif
12025
12026 /* The default buffer size in milliseconds for conservative mode. */
12027 #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
12028 #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
12029 #endif
12030
12031 /* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */
12032 #ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER
12033 #if MA_MAX_FILTER_ORDER >= 4
12034 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4
12035 #else
12036 #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER
12037 #endif
12038 #endif
12039
12040
12041 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
12042 #pragma GCC diagnostic push
12043 #pragma GCC diagnostic ignored "-Wunused-variable"
12044 #endif
12045
12046 /* Standard sample rates, in order of priority. */
12047 static ma_uint32 g_maStandardSampleRatePriorities[] = {
12048 (ma_uint32)ma_standard_sample_rate_48000,
12049 (ma_uint32)ma_standard_sample_rate_44100,
12050
12051 (ma_uint32)ma_standard_sample_rate_32000,
12052 (ma_uint32)ma_standard_sample_rate_24000,
12053 (ma_uint32)ma_standard_sample_rate_22050,
12054
12055 (ma_uint32)ma_standard_sample_rate_88200,
12056 (ma_uint32)ma_standard_sample_rate_96000,
12057 (ma_uint32)ma_standard_sample_rate_176400,
12058 (ma_uint32)ma_standard_sample_rate_192000,
12059
12060 (ma_uint32)ma_standard_sample_rate_16000,
12061 (ma_uint32)ma_standard_sample_rate_11025,
12062 (ma_uint32)ma_standard_sample_rate_8000,
12063
12064 (ma_uint32)ma_standard_sample_rate_352800,
12065 (ma_uint32)ma_standard_sample_rate_384000
12066 };
12067
12068 static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate)
12069 {
12070 ma_uint32 iSampleRate;
12071
12072 for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) {
12073 if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) {
12074 return MA_TRUE;
12075 }
12076 }
12077
12078 /* Getting here means the sample rate is not supported. */
12079 return MA_FALSE;
12080 }
12081
12082
12083 static ma_format g_maFormatPriorities[] = {
12084 ma_format_s16, /* Most common */
12085 ma_format_f32,
12086
12087 /*ma_format_s24_32,*/ /* Clean alignment */
12088 ma_format_s32,
12089
12090 ma_format_s24, /* Unclean alignment */
12091
12092 ma_format_u8 /* Low quality */
12093 };
12094 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
12095 #pragma GCC diagnostic pop
12096 #endif
12097
12098
12099 MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
12100 {
12101 if (pMajor) {
12102 *pMajor = MA_VERSION_MAJOR;
12103 }
12104
12105 if (pMinor) {
12106 *pMinor = MA_VERSION_MINOR;
12107 }
12108
12109 if (pRevision) {
12110 *pRevision = MA_VERSION_REVISION;
12111 }
12112 }
12113
12114 MA_API const char* ma_version_string(void)
12115 {
12116 return MA_VERSION_STRING;
12117 }
12118
12119
12120 /******************************************************************************
12121
12122 Standard Library Stuff
12123
12124 ******************************************************************************/
12125 #ifndef MA_ASSERT
12126 #define MA_ASSERT(condition) assert(condition)
12127 #endif
12128
12129 #ifndef MA_MALLOC
12130 #define MA_MALLOC(sz) malloc((sz))
12131 #endif
12132 #ifndef MA_REALLOC
12133 #define MA_REALLOC(p, sz) realloc((p), (sz))
12134 #endif
12135 #ifndef MA_FREE
12136 #define MA_FREE(p) free((p))
12137 #endif
12138
12139 static MA_INLINE void ma_zero_memory_default(void* p, size_t sz)
12140 {
12141 if (p == NULL) {
12142 MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */
12143 return;
12144 }
12145
12146 if (sz > 0) {
12147 memset(p, 0, sz);
12148 }
12149 }
12150
12151
12152 #ifndef MA_ZERO_MEMORY
12153 #define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz))
12154 #endif
12155 #ifndef MA_COPY_MEMORY
12156 #define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
12157 #endif
12158 #ifndef MA_MOVE_MEMORY
12159 #define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
12160 #endif
12161
12162 #define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
12163
12164 #define ma_countof(x) (sizeof(x) / sizeof(x[0]))
12165 #define ma_max(x, y) (((x) > (y)) ? (x) : (y))
12166 #define ma_min(x, y) (((x) < (y)) ? (x) : (y))
12167 #define ma_abs(x) (((x) > 0) ? (x) : -(x))
12168 #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
12169 #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
12170 #define ma_align(x, a) ((x + (a-1)) & ~(a-1))
12171 #define ma_align_64(x) ma_align(x, 8)
12172
12173 #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
12174
12175 static MA_INLINE double ma_sind(double x)
12176 {
12177 /* TODO: Implement custom sin(x). */
12178 return sin(x);
12179 }
12180
12181 static MA_INLINE double ma_expd(double x)
12182 {
12183 /* TODO: Implement custom exp(x). */
12184 return exp(x);
12185 }
12186
12187 static MA_INLINE double ma_logd(double x)
12188 {
12189 /* TODO: Implement custom log(x). */
12190 return log(x);
12191 }
12192
12193 static MA_INLINE double ma_powd(double x, double y)
12194 {
12195 /* TODO: Implement custom pow(x, y). */
12196 return pow(x, y);
12197 }
12198
12199 static MA_INLINE double ma_sqrtd(double x)
12200 {
12201 /* TODO: Implement custom sqrt(x). */
12202 return sqrt(x);
12203 }
12204
12205
12206 static MA_INLINE float ma_rsqrtf(float x)
12207 {
12208 #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))
12209 {
12210 /*
12211 For SSE we can use RSQRTSS.
12212
12213 This Stack Overflow post suggests that compilers don't necessarily generate optimal code
12214 when using intrinsics:
12215
12216 https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper
12217
12218 I'm going to do something similar here, but a bit simpler.
12219 */
12220 #if defined(__GNUC__) || defined(__clang__)
12221 {
12222 float result;
12223 __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x));
12224 return result;
12225 }
12226 #else
12227 {
12228 return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x)));
12229 }
12230 #endif
12231 }
12232 #else
12233 {
12234 return 1 / (float)ma_sqrtd(x);
12235 }
12236 #endif
12237 }
12238
12239
12240 static MA_INLINE float ma_sinf(float x)
12241 {
12242 return (float)ma_sind((float)x);
12243 }
12244
12245 static MA_INLINE double ma_cosd(double x)
12246 {
12247 return ma_sind((MA_PI_D*0.5) - x);
12248 }
12249
12250 static MA_INLINE float ma_cosf(float x)
12251 {
12252 return (float)ma_cosd((float)x);
12253 }
12254
12255 static MA_INLINE double ma_log10d(double x)
12256 {
12257 return ma_logd(x) * 0.43429448190325182765;
12258 }
12259
12260 static MA_INLINE float ma_powf(float x, float y)
12261 {
12262 return (float)ma_powd((double)x, (double)y);
12263 }
12264
12265 static MA_INLINE float ma_log10f(float x)
12266 {
12267 return (float)ma_log10d((double)x);
12268 }
12269
12270
12271 static MA_INLINE double ma_degrees_to_radians(double degrees)
12272 {
12273 return degrees * 0.01745329252;
12274 }
12275
12276 static MA_INLINE double ma_radians_to_degrees(double radians)
12277 {
12278 return radians * 57.295779512896;
12279 }
12280
12281 static MA_INLINE float ma_degrees_to_radians_f(float degrees)
12282 {
12283 return degrees * 0.01745329252f;
12284 }
12285
12286 static MA_INLINE float ma_radians_to_degrees_f(float radians)
12287 {
12288 return radians * 57.295779512896f;
12289 }
12290
12291
12292 /*
12293 Return Values:
12294 0: Success
12295 22: EINVAL
12296 34: ERANGE
12297
12298 Not using symbolic constants for errors because I want to avoid #including errno.h
12299 */
12300 MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
12301 {
12302 size_t i;
12303
12304 if (dst == 0) {
12305 return 22;
12306 }
12307 if (dstSizeInBytes == 0) {
12308 return 34;
12309 }
12310 if (src == 0) {
12311 dst[0] = '\0';
12312 return 22;
12313 }
12314
12315 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
12316 dst[i] = src[i];
12317 }
12318
12319 if (i < dstSizeInBytes) {
12320 dst[i] = '\0';
12321 return 0;
12322 }
12323
12324 dst[0] = '\0';
12325 return 34;
12326 }
12327
12328 MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src)
12329 {
12330 size_t i;
12331
12332 if (dst == 0) {
12333 return 22;
12334 }
12335 if (dstCap == 0) {
12336 return 34;
12337 }
12338 if (src == 0) {
12339 dst[0] = '\0';
12340 return 22;
12341 }
12342
12343 for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
12344 dst[i] = src[i];
12345 }
12346
12347 if (i < dstCap) {
12348 dst[i] = '\0';
12349 return 0;
12350 }
12351
12352 dst[0] = '\0';
12353 return 34;
12354 }
12355
12356
12357 MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
12358 {
12359 size_t maxcount;
12360 size_t i;
12361
12362 if (dst == 0) {
12363 return 22;
12364 }
12365 if (dstSizeInBytes == 0) {
12366 return 34;
12367 }
12368 if (src == 0) {
12369 dst[0] = '\0';
12370 return 22;
12371 }
12372
12373 maxcount = count;
12374 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
12375 maxcount = dstSizeInBytes - 1;
12376 }
12377
12378 for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
12379 dst[i] = src[i];
12380 }
12381
12382 if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
12383 dst[i] = '\0';
12384 return 0;
12385 }
12386
12387 dst[0] = '\0';
12388 return 34;
12389 }
12390
12391 MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
12392 {
12393 char* dstorig;
12394
12395 if (dst == 0) {
12396 return 22;
12397 }
12398 if (dstSizeInBytes == 0) {
12399 return 34;
12400 }
12401 if (src == 0) {
12402 dst[0] = '\0';
12403 return 22;
12404 }
12405
12406 dstorig = dst;
12407
12408 while (dstSizeInBytes > 0 && dst[0] != '\0') {
12409 dst += 1;
12410 dstSizeInBytes -= 1;
12411 }
12412
12413 if (dstSizeInBytes == 0) {
12414 return 22; /* Unterminated. */
12415 }
12416
12417
12418 while (dstSizeInBytes > 0 && src[0] != '\0') {
12419 *dst++ = *src++;
12420 dstSizeInBytes -= 1;
12421 }
12422
12423 if (dstSizeInBytes > 0) {
12424 dst[0] = '\0';
12425 } else {
12426 dstorig[0] = '\0';
12427 return 34;
12428 }
12429
12430 return 0;
12431 }
12432
12433 MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
12434 {
12435 char* dstorig;
12436
12437 if (dst == 0) {
12438 return 22;
12439 }
12440 if (dstSizeInBytes == 0) {
12441 return 34;
12442 }
12443 if (src == 0) {
12444 return 22;
12445 }
12446
12447 dstorig = dst;
12448
12449 while (dstSizeInBytes > 0 && dst[0] != '\0') {
12450 dst += 1;
12451 dstSizeInBytes -= 1;
12452 }
12453
12454 if (dstSizeInBytes == 0) {
12455 return 22; /* Unterminated. */
12456 }
12457
12458
12459 if (count == ((size_t)-1)) { /* _TRUNCATE */
12460 count = dstSizeInBytes - 1;
12461 }
12462
12463 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
12464 *dst++ = *src++;
12465 dstSizeInBytes -= 1;
12466 count -= 1;
12467 }
12468
12469 if (dstSizeInBytes > 0) {
12470 dst[0] = '\0';
12471 } else {
12472 dstorig[0] = '\0';
12473 return 34;
12474 }
12475
12476 return 0;
12477 }
12478
12479 MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
12480 {
12481 int sign;
12482 unsigned int valueU;
12483 char* dstEnd;
12484
12485 if (dst == NULL || dstSizeInBytes == 0) {
12486 return 22;
12487 }
12488 if (radix < 2 || radix > 36) {
12489 dst[0] = '\0';
12490 return 22;
12491 }
12492
12493 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
12494
12495 if (value < 0) {
12496 valueU = -value;
12497 } else {
12498 valueU = value;
12499 }
12500
12501 dstEnd = dst;
12502 do
12503 {
12504 int remainder = valueU % radix;
12505 if (remainder > 9) {
12506 *dstEnd = (char)((remainder - 10) + 'a');
12507 } else {
12508 *dstEnd = (char)(remainder + '0');
12509 }
12510
12511 dstEnd += 1;
12512 dstSizeInBytes -= 1;
12513 valueU /= radix;
12514 } while (dstSizeInBytes > 0 && valueU > 0);
12515
12516 if (dstSizeInBytes == 0) {
12517 dst[0] = '\0';
12518 return 22; /* Ran out of room in the output buffer. */
12519 }
12520
12521 if (sign < 0) {
12522 *dstEnd++ = '-';
12523 dstSizeInBytes -= 1;
12524 }
12525
12526 if (dstSizeInBytes == 0) {
12527 dst[0] = '\0';
12528 return 22; /* Ran out of room in the output buffer. */
12529 }
12530
12531 *dstEnd = '\0';
12532
12533
12534 /* At this point the string will be reversed. */
12535 dstEnd -= 1;
12536 while (dst < dstEnd) {
12537 char temp = *dst;
12538 *dst = *dstEnd;
12539 *dstEnd = temp;
12540
12541 dst += 1;
12542 dstEnd -= 1;
12543 }
12544
12545 return 0;
12546 }
12547
12548 MA_API int ma_strcmp(const char* str1, const char* str2)
12549 {
12550 if (str1 == str2) return 0;
12551
12552 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
12553 if (str1 == NULL) return -1;
12554 if (str2 == NULL) return 1;
12555
12556 for (;;) {
12557 if (str1[0] == '\0') {
12558 break;
12559 }
12560 if (str1[0] != str2[0]) {
12561 break;
12562 }
12563
12564 str1 += 1;
12565 str2 += 1;
12566 }
12567
12568 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
12569 }
12570
12571 MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
12572 {
12573 int result;
12574
12575 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
12576 if (result != 0) {
12577 return result;
12578 }
12579
12580 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
12581 if (result != 0) {
12582 return result;
12583 }
12584
12585 return result;
12586 }
12587
12588 MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
12589 {
12590 size_t sz;
12591 char* dst;
12592
12593 if (src == NULL) {
12594 return NULL;
12595 }
12596
12597 sz = strlen(src)+1;
12598 dst = (char*)ma_malloc(sz, pAllocationCallbacks);
12599 if (dst == NULL) {
12600 return NULL;
12601 }
12602
12603 ma_strcpy_s(dst, sz, src);
12604
12605 return dst;
12606 }
12607
12608 MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks)
12609 {
12610 size_t sz = wcslen(src)+1;
12611 wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks);
12612 if (dst == NULL) {
12613 return NULL;
12614 }
12615
12616 ma_wcscpy_s(dst, sz, src);
12617
12618 return dst;
12619 }
12620
12621
12622
12623 #include <errno.h>
12624 static ma_result ma_result_from_errno(int e)
12625 {
12626 if (e == 0) {
12627 return MA_SUCCESS;
12628 }
12629 #ifdef EPERM
12630 else if (e == EPERM) { return MA_INVALID_OPERATION; }
12631 #endif
12632 #ifdef ENOENT
12633 else if (e == ENOENT) { return MA_DOES_NOT_EXIST; }
12634 #endif
12635 #ifdef ESRCH
12636 else if (e == ESRCH) { return MA_DOES_NOT_EXIST; }
12637 #endif
12638 #ifdef EINTR
12639 else if (e == EINTR) { return MA_INTERRUPT; }
12640 #endif
12641 #ifdef EIO
12642 else if (e == EIO) { return MA_IO_ERROR; }
12643 #endif
12644 #ifdef ENXIO
12645 else if (e == ENXIO) { return MA_DOES_NOT_EXIST; }
12646 #endif
12647 #ifdef E2BIG
12648 else if (e == E2BIG) { return MA_INVALID_ARGS; }
12649 #endif
12650 #ifdef ENOEXEC
12651 else if (e == ENOEXEC) { return MA_INVALID_FILE; }
12652 #endif
12653 #ifdef EBADF
12654 else if (e == EBADF) { return MA_INVALID_FILE; }
12655 #endif
12656 #ifdef ECHILD
12657 else if (e == ECHILD) { return MA_ERROR; }
12658 #endif
12659 #ifdef EAGAIN
12660 else if (e == EAGAIN) { return MA_UNAVAILABLE; }
12661 #endif
12662 #ifdef ENOMEM
12663 else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; }
12664 #endif
12665 #ifdef EACCES
12666 else if (e == EACCES) { return MA_ACCESS_DENIED; }
12667 #endif
12668 #ifdef EFAULT
12669 else if (e == EFAULT) { return MA_BAD_ADDRESS; }
12670 #endif
12671 #ifdef ENOTBLK
12672 else if (e == ENOTBLK) { return MA_ERROR; }
12673 #endif
12674 #ifdef EBUSY
12675 else if (e == EBUSY) { return MA_BUSY; }
12676 #endif
12677 #ifdef EEXIST
12678 else if (e == EEXIST) { return MA_ALREADY_EXISTS; }
12679 #endif
12680 #ifdef EXDEV
12681 else if (e == EXDEV) { return MA_ERROR; }
12682 #endif
12683 #ifdef ENODEV
12684 else if (e == ENODEV) { return MA_DOES_NOT_EXIST; }
12685 #endif
12686 #ifdef ENOTDIR
12687 else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; }
12688 #endif
12689 #ifdef EISDIR
12690 else if (e == EISDIR) { return MA_IS_DIRECTORY; }
12691 #endif
12692 #ifdef EINVAL
12693 else if (e == EINVAL) { return MA_INVALID_ARGS; }
12694 #endif
12695 #ifdef ENFILE
12696 else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; }
12697 #endif
12698 #ifdef EMFILE
12699 else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; }
12700 #endif
12701 #ifdef ENOTTY
12702 else if (e == ENOTTY) { return MA_INVALID_OPERATION; }
12703 #endif
12704 #ifdef ETXTBSY
12705 else if (e == ETXTBSY) { return MA_BUSY; }
12706 #endif
12707 #ifdef EFBIG
12708 else if (e == EFBIG) { return MA_TOO_BIG; }
12709 #endif
12710 #ifdef ENOSPC
12711 else if (e == ENOSPC) { return MA_NO_SPACE; }
12712 #endif
12713 #ifdef ESPIPE
12714 else if (e == ESPIPE) { return MA_BAD_SEEK; }
12715 #endif
12716 #ifdef EROFS
12717 else if (e == EROFS) { return MA_ACCESS_DENIED; }
12718 #endif
12719 #ifdef EMLINK
12720 else if (e == EMLINK) { return MA_TOO_MANY_LINKS; }
12721 #endif
12722 #ifdef EPIPE
12723 else if (e == EPIPE) { return MA_BAD_PIPE; }
12724 #endif
12725 #ifdef EDOM
12726 else if (e == EDOM) { return MA_OUT_OF_RANGE; }
12727 #endif
12728 #ifdef ERANGE
12729 else if (e == ERANGE) { return MA_OUT_OF_RANGE; }
12730 #endif
12731 #ifdef EDEADLK
12732 else if (e == EDEADLK) { return MA_DEADLOCK; }
12733 #endif
12734 #ifdef ENAMETOOLONG
12735 else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; }
12736 #endif
12737 #ifdef ENOLCK
12738 else if (e == ENOLCK) { return MA_ERROR; }
12739 #endif
12740 #ifdef ENOSYS
12741 else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; }
12742 #endif
12743 #ifdef ENOTEMPTY
12744 else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; }
12745 #endif
12746 #ifdef ELOOP
12747 else if (e == ELOOP) { return MA_TOO_MANY_LINKS; }
12748 #endif
12749 #ifdef ENOMSG
12750 else if (e == ENOMSG) { return MA_NO_MESSAGE; }
12751 #endif
12752 #ifdef EIDRM
12753 else if (e == EIDRM) { return MA_ERROR; }
12754 #endif
12755 #ifdef ECHRNG
12756 else if (e == ECHRNG) { return MA_ERROR; }
12757 #endif
12758 #ifdef EL2NSYNC
12759 else if (e == EL2NSYNC) { return MA_ERROR; }
12760 #endif
12761 #ifdef EL3HLT
12762 else if (e == EL3HLT) { return MA_ERROR; }
12763 #endif
12764 #ifdef EL3RST
12765 else if (e == EL3RST) { return MA_ERROR; }
12766 #endif
12767 #ifdef ELNRNG
12768 else if (e == ELNRNG) { return MA_OUT_OF_RANGE; }
12769 #endif
12770 #ifdef EUNATCH
12771 else if (e == EUNATCH) { return MA_ERROR; }
12772 #endif
12773 #ifdef ENOCSI
12774 else if (e == ENOCSI) { return MA_ERROR; }
12775 #endif
12776 #ifdef EL2HLT
12777 else if (e == EL2HLT) { return MA_ERROR; }
12778 #endif
12779 #ifdef EBADE
12780 else if (e == EBADE) { return MA_ERROR; }
12781 #endif
12782 #ifdef EBADR
12783 else if (e == EBADR) { return MA_ERROR; }
12784 #endif
12785 #ifdef EXFULL
12786 else if (e == EXFULL) { return MA_ERROR; }
12787 #endif
12788 #ifdef ENOANO
12789 else if (e == ENOANO) { return MA_ERROR; }
12790 #endif
12791 #ifdef EBADRQC
12792 else if (e == EBADRQC) { return MA_ERROR; }
12793 #endif
12794 #ifdef EBADSLT
12795 else if (e == EBADSLT) { return MA_ERROR; }
12796 #endif
12797 #ifdef EBFONT
12798 else if (e == EBFONT) { return MA_INVALID_FILE; }
12799 #endif
12800 #ifdef ENOSTR
12801 else if (e == ENOSTR) { return MA_ERROR; }
12802 #endif
12803 #ifdef ENODATA
12804 else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; }
12805 #endif
12806 #ifdef ETIME
12807 else if (e == ETIME) { return MA_TIMEOUT; }
12808 #endif
12809 #ifdef ENOSR
12810 else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; }
12811 #endif
12812 #ifdef ENONET
12813 else if (e == ENONET) { return MA_NO_NETWORK; }
12814 #endif
12815 #ifdef ENOPKG
12816 else if (e == ENOPKG) { return MA_ERROR; }
12817 #endif
12818 #ifdef EREMOTE
12819 else if (e == EREMOTE) { return MA_ERROR; }
12820 #endif
12821 #ifdef ENOLINK
12822 else if (e == ENOLINK) { return MA_ERROR; }
12823 #endif
12824 #ifdef EADV
12825 else if (e == EADV) { return MA_ERROR; }
12826 #endif
12827 #ifdef ESRMNT
12828 else if (e == ESRMNT) { return MA_ERROR; }
12829 #endif
12830 #ifdef ECOMM
12831 else if (e == ECOMM) { return MA_ERROR; }
12832 #endif
12833 #ifdef EPROTO
12834 else if (e == EPROTO) { return MA_ERROR; }
12835 #endif
12836 #ifdef EMULTIHOP
12837 else if (e == EMULTIHOP) { return MA_ERROR; }
12838 #endif
12839 #ifdef EDOTDOT
12840 else if (e == EDOTDOT) { return MA_ERROR; }
12841 #endif
12842 #ifdef EBADMSG
12843 else if (e == EBADMSG) { return MA_BAD_MESSAGE; }
12844 #endif
12845 #ifdef EOVERFLOW
12846 else if (e == EOVERFLOW) { return MA_TOO_BIG; }
12847 #endif
12848 #ifdef ENOTUNIQ
12849 else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; }
12850 #endif
12851 #ifdef EBADFD
12852 else if (e == EBADFD) { return MA_ERROR; }
12853 #endif
12854 #ifdef EREMCHG
12855 else if (e == EREMCHG) { return MA_ERROR; }
12856 #endif
12857 #ifdef ELIBACC
12858 else if (e == ELIBACC) { return MA_ACCESS_DENIED; }
12859 #endif
12860 #ifdef ELIBBAD
12861 else if (e == ELIBBAD) { return MA_INVALID_FILE; }
12862 #endif
12863 #ifdef ELIBSCN
12864 else if (e == ELIBSCN) { return MA_INVALID_FILE; }
12865 #endif
12866 #ifdef ELIBMAX
12867 else if (e == ELIBMAX) { return MA_ERROR; }
12868 #endif
12869 #ifdef ELIBEXEC
12870 else if (e == ELIBEXEC) { return MA_ERROR; }
12871 #endif
12872 #ifdef EILSEQ
12873 else if (e == EILSEQ) { return MA_INVALID_DATA; }
12874 #endif
12875 #ifdef ERESTART
12876 else if (e == ERESTART) { return MA_ERROR; }
12877 #endif
12878 #ifdef ESTRPIPE
12879 else if (e == ESTRPIPE) { return MA_ERROR; }
12880 #endif
12881 #ifdef EUSERS
12882 else if (e == EUSERS) { return MA_ERROR; }
12883 #endif
12884 #ifdef ENOTSOCK
12885 else if (e == ENOTSOCK) { return MA_NOT_SOCKET; }
12886 #endif
12887 #ifdef EDESTADDRREQ
12888 else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; }
12889 #endif
12890 #ifdef EMSGSIZE
12891 else if (e == EMSGSIZE) { return MA_TOO_BIG; }
12892 #endif
12893 #ifdef EPROTOTYPE
12894 else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; }
12895 #endif
12896 #ifdef ENOPROTOOPT
12897 else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; }
12898 #endif
12899 #ifdef EPROTONOSUPPORT
12900 else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; }
12901 #endif
12902 #ifdef ESOCKTNOSUPPORT
12903 else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; }
12904 #endif
12905 #ifdef EOPNOTSUPP
12906 else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; }
12907 #endif
12908 #ifdef EPFNOSUPPORT
12909 else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; }
12910 #endif
12911 #ifdef EAFNOSUPPORT
12912 else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; }
12913 #endif
12914 #ifdef EADDRINUSE
12915 else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; }
12916 #endif
12917 #ifdef EADDRNOTAVAIL
12918 else if (e == EADDRNOTAVAIL) { return MA_ERROR; }
12919 #endif
12920 #ifdef ENETDOWN
12921 else if (e == ENETDOWN) { return MA_NO_NETWORK; }
12922 #endif
12923 #ifdef ENETUNREACH
12924 else if (e == ENETUNREACH) { return MA_NO_NETWORK; }
12925 #endif
12926 #ifdef ENETRESET
12927 else if (e == ENETRESET) { return MA_NO_NETWORK; }
12928 #endif
12929 #ifdef ECONNABORTED
12930 else if (e == ECONNABORTED) { return MA_NO_NETWORK; }
12931 #endif
12932 #ifdef ECONNRESET
12933 else if (e == ECONNRESET) { return MA_CONNECTION_RESET; }
12934 #endif
12935 #ifdef ENOBUFS
12936 else if (e == ENOBUFS) { return MA_NO_SPACE; }
12937 #endif
12938 #ifdef EISCONN
12939 else if (e == EISCONN) { return MA_ALREADY_CONNECTED; }
12940 #endif
12941 #ifdef ENOTCONN
12942 else if (e == ENOTCONN) { return MA_NOT_CONNECTED; }
12943 #endif
12944 #ifdef ESHUTDOWN
12945 else if (e == ESHUTDOWN) { return MA_ERROR; }
12946 #endif
12947 #ifdef ETOOMANYREFS
12948 else if (e == ETOOMANYREFS) { return MA_ERROR; }
12949 #endif
12950 #ifdef ETIMEDOUT
12951 else if (e == ETIMEDOUT) { return MA_TIMEOUT; }
12952 #endif
12953 #ifdef ECONNREFUSED
12954 else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; }
12955 #endif
12956 #ifdef EHOSTDOWN
12957 else if (e == EHOSTDOWN) { return MA_NO_HOST; }
12958 #endif
12959 #ifdef EHOSTUNREACH
12960 else if (e == EHOSTUNREACH) { return MA_NO_HOST; }
12961 #endif
12962 #ifdef EALREADY
12963 else if (e == EALREADY) { return MA_IN_PROGRESS; }
12964 #endif
12965 #ifdef EINPROGRESS
12966 else if (e == EINPROGRESS) { return MA_IN_PROGRESS; }
12967 #endif
12968 #ifdef ESTALE
12969 else if (e == ESTALE) { return MA_INVALID_FILE; }
12970 #endif
12971 #ifdef EUCLEAN
12972 else if (e == EUCLEAN) { return MA_ERROR; }
12973 #endif
12974 #ifdef ENOTNAM
12975 else if (e == ENOTNAM) { return MA_ERROR; }
12976 #endif
12977 #ifdef ENAVAIL
12978 else if (e == ENAVAIL) { return MA_ERROR; }
12979 #endif
12980 #ifdef EISNAM
12981 else if (e == EISNAM) { return MA_ERROR; }
12982 #endif
12983 #ifdef EREMOTEIO
12984 else if (e == EREMOTEIO) { return MA_IO_ERROR; }
12985 #endif
12986 #ifdef EDQUOT
12987 else if (e == EDQUOT) { return MA_NO_SPACE; }
12988 #endif
12989 #ifdef ENOMEDIUM
12990 else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; }
12991 #endif
12992 #ifdef EMEDIUMTYPE
12993 else if (e == EMEDIUMTYPE) { return MA_ERROR; }
12994 #endif
12995 #ifdef ECANCELED
12996 else if (e == ECANCELED) { return MA_CANCELLED; }
12997 #endif
12998 #ifdef ENOKEY
12999 else if (e == ENOKEY) { return MA_ERROR; }
13000 #endif
13001 #ifdef EKEYEXPIRED
13002 else if (e == EKEYEXPIRED) { return MA_ERROR; }
13003 #endif
13004 #ifdef EKEYREVOKED
13005 else if (e == EKEYREVOKED) { return MA_ERROR; }
13006 #endif
13007 #ifdef EKEYREJECTED
13008 else if (e == EKEYREJECTED) { return MA_ERROR; }
13009 #endif
13010 #ifdef EOWNERDEAD
13011 else if (e == EOWNERDEAD) { return MA_ERROR; }
13012 #endif
13013 #ifdef ENOTRECOVERABLE
13014 else if (e == ENOTRECOVERABLE) { return MA_ERROR; }
13015 #endif
13016 #ifdef ERFKILL
13017 else if (e == ERFKILL) { return MA_ERROR; }
13018 #endif
13019 #ifdef EHWPOISON
13020 else if (e == EHWPOISON) { return MA_ERROR; }
13021 #endif
13022 else {
13023 return MA_ERROR;
13024 }
13025 }
13026
13027 MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
13028 {
13029 #if defined(_MSC_VER) && _MSC_VER >= 1400
13030 errno_t err;
13031 #endif
13032
13033 if (ppFile != NULL) {
13034 *ppFile = NULL; /* Safety. */
13035 }
13036
13037 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
13038 return MA_INVALID_ARGS;
13039 }
13040
13041 #if defined(_MSC_VER) && _MSC_VER >= 1400
13042 err = fopen_s(ppFile, pFilePath, pOpenMode);
13043 if (err != 0) {
13044 return ma_result_from_errno(err);
13045 }
13046 #else
13047 #if defined(_WIN32) || defined(__APPLE__)
13048 *ppFile = fopen(pFilePath, pOpenMode);
13049 #else
13050 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
13051 *ppFile = fopen64(pFilePath, pOpenMode);
13052 #else
13053 *ppFile = fopen(pFilePath, pOpenMode);
13054 #endif
13055 #endif
13056 if (*ppFile == NULL) {
13057 ma_result result = ma_result_from_errno(errno);
13058 if (result == MA_SUCCESS) {
13059 result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
13060 }
13061
13062 return result;
13063 }
13064 #endif
13065
13066 return MA_SUCCESS;
13067 }
13068
13069
13070
13071 /*
13072 _wfopen() isn't always available in all compilation environments.
13073
13074 * Windows only.
13075 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
13076 * MinGW-64 (both 32- and 64-bit) seems to support it.
13077 * MinGW wraps it in !defined(__STRICT_ANSI__).
13078 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
13079
13080 This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
13081 fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
13082 */
13083 #if defined(_WIN32)
13084 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
13085 #define MA_HAS_WFOPEN
13086 #endif
13087 #endif
13088
13089 MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
13090 {
13091 if (ppFile != NULL) {
13092 *ppFile = NULL; /* Safety. */
13093 }
13094
13095 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
13096 return MA_INVALID_ARGS;
13097 }
13098
13099 #if defined(MA_HAS_WFOPEN)
13100 {
13101 /* Use _wfopen() on Windows. */
13102 #if defined(_MSC_VER) && _MSC_VER >= 1400
13103 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
13104 if (err != 0) {
13105 return ma_result_from_errno(err);
13106 }
13107 #else
13108 *ppFile = _wfopen(pFilePath, pOpenMode);
13109 if (*ppFile == NULL) {
13110 return ma_result_from_errno(errno);
13111 }
13112 #endif
13113 (void)pAllocationCallbacks;
13114 }
13115 #else
13116 /*
13117 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
13118 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
13119 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.
13120 */
13121 {
13122 mbstate_t mbs;
13123 size_t lenMB;
13124 const wchar_t* pFilePathTemp = pFilePath;
13125 char* pFilePathMB = NULL;
13126 char pOpenModeMB[32] = {0};
13127
13128 /* Get the length first. */
13129 MA_ZERO_OBJECT(&mbs);
13130 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
13131 if (lenMB == (size_t)-1) {
13132 return ma_result_from_errno(errno);
13133 }
13134
13135 pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
13136 if (pFilePathMB == NULL) {
13137 return MA_OUT_OF_MEMORY;
13138 }
13139
13140 pFilePathTemp = pFilePath;
13141 MA_ZERO_OBJECT(&mbs);
13142 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
13143
13144 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
13145 {
13146 size_t i = 0;
13147 for (;;) {
13148 if (pOpenMode[i] == 0) {
13149 pOpenModeMB[i] = '\0';
13150 break;
13151 }
13152
13153 pOpenModeMB[i] = (char)pOpenMode[i];
13154 i += 1;
13155 }
13156 }
13157
13158 *ppFile = fopen(pFilePathMB, pOpenModeMB);
13159
13160 ma_free(pFilePathMB, pAllocationCallbacks);
13161 }
13162
13163 if (*ppFile == NULL) {
13164 return MA_ERROR;
13165 }
13166 #endif
13167
13168 return MA_SUCCESS;
13169 }
13170
13171
13172
13173 static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
13174 {
13175 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
13176 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
13177 #else
13178 while (sizeInBytes > 0) {
13179 ma_uint64 bytesToCopyNow = sizeInBytes;
13180 if (bytesToCopyNow > MA_SIZE_MAX) {
13181 bytesToCopyNow = MA_SIZE_MAX;
13182 }
13183
13184 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
13185
13186 sizeInBytes -= bytesToCopyNow;
13187 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
13188 src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
13189 }
13190 #endif
13191 }
13192
13193 static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
13194 {
13195 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
13196 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
13197 #else
13198 while (sizeInBytes > 0) {
13199 ma_uint64 bytesToZeroNow = sizeInBytes;
13200 if (bytesToZeroNow > MA_SIZE_MAX) {
13201 bytesToZeroNow = MA_SIZE_MAX;
13202 }
13203
13204 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
13205
13206 sizeInBytes -= bytesToZeroNow;
13207 dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
13208 }
13209 #endif
13210 }
13211
13212
13213 /* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
13214 static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
13215 {
13216 x--;
13217 x |= x >> 1;
13218 x |= x >> 2;
13219 x |= x >> 4;
13220 x |= x >> 8;
13221 x |= x >> 16;
13222 x++;
13223
13224 return x;
13225 }
13226
13227 static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
13228 {
13229 return ma_next_power_of_2(x) >> 1;
13230 }
13231
13232 static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
13233 {
13234 unsigned int prev = ma_prev_power_of_2(x);
13235 unsigned int next = ma_next_power_of_2(x);
13236 if ((next - x) > (x - prev)) {
13237 return prev;
13238 } else {
13239 return next;
13240 }
13241 }
13242
13243 static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
13244 {
13245 unsigned int count = 0;
13246 while (x != 0) {
13247 if (x & 1) {
13248 count += 1;
13249 }
13250
13251 x = x >> 1;
13252 }
13253
13254 return count;
13255 }
13256
13257
13258
13259 /**************************************************************************************************************************************************************
13260
13261 Allocation Callbacks
13262
13263 **************************************************************************************************************************************************************/
13264 static void* ma__malloc_default(size_t sz, void* pUserData)
13265 {
13266 (void)pUserData;
13267 return MA_MALLOC(sz);
13268 }
13269
13270 static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
13271 {
13272 (void)pUserData;
13273 return MA_REALLOC(p, sz);
13274 }
13275
13276 static void ma__free_default(void* p, void* pUserData)
13277 {
13278 (void)pUserData;
13279 MA_FREE(p);
13280 }
13281
13282 static ma_allocation_callbacks ma_allocation_callbacks_init_default(void)
13283 {
13284 ma_allocation_callbacks callbacks;
13285 callbacks.pUserData = NULL;
13286 callbacks.onMalloc = ma__malloc_default;
13287 callbacks.onRealloc = ma__realloc_default;
13288 callbacks.onFree = ma__free_default;
13289
13290 return callbacks;
13291 }
13292
13293 static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
13294 {
13295 if (pDst == NULL) {
13296 return MA_INVALID_ARGS;
13297 }
13298
13299 if (pSrc == NULL) {
13300 *pDst = ma_allocation_callbacks_init_default();
13301 } else {
13302 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
13303 *pDst = ma_allocation_callbacks_init_default();
13304 } else {
13305 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
13306 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
13307 } else {
13308 *pDst = *pSrc;
13309 }
13310 }
13311 }
13312
13313 return MA_SUCCESS;
13314 }
13315
13316
13317
13318
13319 /**************************************************************************************************************************************************************
13320
13321 Logging
13322
13323 **************************************************************************************************************************************************************/
13324 MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
13325 {
13326 switch (logLevel)
13327 {
13328 case MA_LOG_LEVEL_DEBUG: return "DEBUG";
13329 case MA_LOG_LEVEL_INFO: return "INFO";
13330 case MA_LOG_LEVEL_WARNING: return "WARNING";
13331 case MA_LOG_LEVEL_ERROR: return "ERROR";
13332 default: return "ERROR";
13333 }
13334 }
13335
13336 #if defined(MA_DEBUG_OUTPUT)
13337 #if defined(MA_ANDROID)
13338 #include <android/log.h>
13339 #endif
13340
13341 /* Customize this to use a specific tag in __android_log_print() for debug output messages. */
13342 #ifndef MA_ANDROID_LOG_TAG
13343 #define MA_ANDROID_LOG_TAG "miniaudio"
13344 #endif
13345
13346 void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)
13347 {
13348 (void)pUserData;
13349
13350 /* Special handling for some platforms. */
13351 #if defined(MA_ANDROID)
13352 {
13353 /* Android. */
13354 __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage);
13355 }
13356 #else
13357 {
13358 /* Everything else. */
13359 printf("%s: %s", ma_log_level_to_string(level), pMessage);
13360 }
13361 #endif
13362 }
13363 #endif
13364
13365 MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData)
13366 {
13367 ma_log_callback callback;
13368
13369 MA_ZERO_OBJECT(&callback);
13370 callback.onLog = onLog;
13371 callback.pUserData = pUserData;
13372
13373 return callback;
13374 }
13375
13376
13377 MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)
13378 {
13379 if (pLog == NULL) {
13380 return MA_INVALID_ARGS;
13381 }
13382
13383 MA_ZERO_OBJECT(pLog);
13384 ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);
13385
13386 /* We need a mutex for thread safety. */
13387 #ifndef MA_NO_THREADING
13388 {
13389 ma_result result = ma_mutex_init(&pLog->lock);
13390 if (result != MA_SUCCESS) {
13391 return result;
13392 }
13393 }
13394 #endif
13395
13396 /* If we're using debug output, enable it. */
13397 #if defined(MA_DEBUG_OUTPUT)
13398 {
13399 ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */
13400 }
13401 #endif
13402
13403 return MA_SUCCESS;
13404 }
13405
13406 MA_API void ma_log_uninit(ma_log* pLog)
13407 {
13408 if (pLog == NULL) {
13409 return;
13410 }
13411
13412 #ifndef MA_NO_THREADING
13413 ma_mutex_uninit(&pLog->lock);
13414 #endif
13415 }
13416
13417 static void ma_log_lock(ma_log* pLog)
13418 {
13419 #ifndef MA_NO_THREADING
13420 ma_mutex_lock(&pLog->lock);
13421 #else
13422 (void)pLog;
13423 #endif
13424 }
13425
13426 static void ma_log_unlock(ma_log* pLog)
13427 {
13428 #ifndef MA_NO_THREADING
13429 ma_mutex_unlock(&pLog->lock);
13430 #else
13431 (void)pLog;
13432 #endif
13433 }
13434
13435 MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback)
13436 {
13437 ma_result result = MA_SUCCESS;
13438
13439 if (pLog == NULL || callback.onLog == NULL) {
13440 return MA_INVALID_ARGS;
13441 }
13442
13443 ma_log_lock(pLog);
13444 {
13445 if (pLog->callbackCount == ma_countof(pLog->callbacks)) {
13446 result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */
13447 } else {
13448 pLog->callbacks[pLog->callbackCount] = callback;
13449 pLog->callbackCount += 1;
13450 }
13451 }
13452 ma_log_unlock(pLog);
13453
13454 return result;
13455 }
13456
13457 MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback)
13458 {
13459 if (pLog == NULL) {
13460 return MA_INVALID_ARGS;
13461 }
13462
13463 ma_log_lock(pLog);
13464 {
13465 ma_uint32 iLog;
13466 for (iLog = 0; iLog < pLog->callbackCount; ) {
13467 if (pLog->callbacks[iLog].onLog == callback.onLog) {
13468 /* Found. Move everything down a slot. */
13469 ma_uint32 jLog;
13470 for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) {
13471 pLog->callbacks[jLog] = pLog->callbacks[jLog + 1];
13472 }
13473
13474 pLog->callbackCount -= 1;
13475 } else {
13476 /* Not found. */
13477 iLog += 1;
13478 }
13479 }
13480 }
13481 ma_log_unlock(pLog);
13482
13483 return MA_SUCCESS;
13484 }
13485
13486 MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage)
13487 {
13488 if (pLog == NULL || pMessage == NULL) {
13489 return MA_INVALID_ARGS;
13490 }
13491
13492 ma_log_lock(pLog);
13493 {
13494 ma_uint32 iLog;
13495 for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) {
13496 if (pLog->callbacks[iLog].onLog) {
13497 pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage);
13498 }
13499 }
13500 }
13501 ma_log_unlock(pLog);
13502
13503 return MA_SUCCESS;
13504 }
13505
13506
13507 /*
13508 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
13509 logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
13510 */
13511 #if defined(_MSC_VER) && _MSC_VER < 1900
13512 static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args)
13513 {
13514 #if _MSC_VER > 1200
13515 return _vscprintf(format, args);
13516 #else
13517 int result;
13518 char* pTempBuffer = NULL;
13519 size_t tempBufferCap = 1024;
13520
13521 if (format == NULL) {
13522 errno = EINVAL;
13523 return -1;
13524 }
13525
13526 for (;;) {
13527 char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks);
13528 if (pNewTempBuffer == NULL) {
13529 ma_free(pTempBuffer, pAllocationCallbacks);
13530 errno = ENOMEM;
13531 return -1; /* Out of memory. */
13532 }
13533
13534 pTempBuffer = pNewTempBuffer;
13535
13536 result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
13537 ma_free(pTempBuffer, NULL);
13538
13539 if (result != -1) {
13540 break; /* Got it. */
13541 }
13542
13543 /* 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. */
13544 tempBufferCap *= 2;
13545 }
13546
13547 return result;
13548 #endif
13549 }
13550 #endif
13551
13552 MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args)
13553 {
13554 if (pLog == NULL || pFormat == NULL) {
13555 return MA_INVALID_ARGS;
13556 }
13557
13558 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L)
13559 {
13560 ma_result result;
13561 int length;
13562 char pFormattedMessageStack[1024];
13563 char* pFormattedMessageHeap = NULL;
13564
13565 /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */
13566 length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args);
13567 if (length < 0) {
13568 return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */
13569 }
13570
13571 if ((size_t)length < sizeof(pFormattedMessageStack)) {
13572 /* The string was written to the stack. */
13573 result = ma_log_post(pLog, level, pFormattedMessageStack);
13574 } else {
13575 /* The stack buffer was too small, try the heap. */
13576 pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks);
13577 if (pFormattedMessageHeap == NULL) {
13578 return MA_OUT_OF_MEMORY;
13579 }
13580
13581 length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args);
13582 if (length < 0) {
13583 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
13584 return MA_INVALID_OPERATION;
13585 }
13586
13587 result = ma_log_post(pLog, level, pFormattedMessageHeap);
13588 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
13589 }
13590
13591 return result;
13592 }
13593 #else
13594 {
13595 /*
13596 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
13597 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
13598 a fixed sized stack allocated buffer.
13599 */
13600 #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */
13601 {
13602 ma_result result;
13603 int formattedLen;
13604 char* pFormattedMessage = NULL;
13605 va_list args2;
13606
13607 #if _MSC_VER >= 1800
13608 {
13609 va_copy(args2, args);
13610 }
13611 #else
13612 {
13613 args2 = args;
13614 }
13615 #endif
13616
13617 formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2);
13618 va_end(args2);
13619
13620 if (formattedLen <= 0) {
13621 return MA_INVALID_OPERATION;
13622 }
13623
13624 pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks);
13625 if (pFormattedMessage == NULL) {
13626 return MA_OUT_OF_MEMORY;
13627 }
13628
13629 /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */
13630 #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */
13631 {
13632 vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
13633 }
13634 #else
13635 {
13636 vsprintf(pFormattedMessage, pFormat, args);
13637 }
13638 #endif
13639
13640 result = ma_log_post(pLog, level, pFormattedMessage);
13641 ma_free(pFormattedMessage, &pLog->allocationCallbacks);
13642
13643 return result;
13644 }
13645 #else
13646 {
13647 /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */
13648 (void)level;
13649 (void)args;
13650
13651 return MA_INVALID_OPERATION;
13652 }
13653 #endif
13654 }
13655 #endif
13656 }
13657
13658 MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...)
13659 {
13660 ma_result result;
13661 va_list args;
13662
13663 if (pLog == NULL || pFormat == NULL) {
13664 return MA_INVALID_ARGS;
13665 }
13666
13667 va_start(args, pFormat);
13668 {
13669 result = ma_log_postv(pLog, level, pFormat, args);
13670 }
13671 va_end(args);
13672
13673 return result;
13674 }
13675
13676
13677
13678 static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x)
13679 {
13680 return (ma_uint8)(ma_clamp(x, -128, 127) + 128);
13681 }
13682
13683 static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)
13684 {
13685 return (ma_int16)ma_clamp(x, -32768, 32767);
13686 }
13687
13688 static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)
13689 {
13690 return (ma_int64)ma_clamp(x, -8388608, 8388607);
13691 }
13692
13693 static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)
13694 {
13695 /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */
13696 ma_int64 clipMin;
13697 ma_int64 clipMax;
13698 clipMin = -((ma_int64)2147483647 + 1);
13699 clipMax = (ma_int64)2147483647;
13700
13701 return (ma_int32)ma_clamp(x, clipMin, clipMax);
13702 }
13703
13704 static MA_INLINE float ma_clip_f32(float x)
13705 {
13706 if (x < -1) return -1;
13707 if (x > +1) return +1;
13708 return x;
13709 }
13710
13711
13712 static MA_INLINE float ma_mix_f32(float x, float y, float a)
13713 {
13714 return x*(1-a) + y*a;
13715 }
13716 static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
13717 {
13718 float r0 = (y - x);
13719 float r1 = r0*a;
13720 return x + r1;
13721 /*return x + (y - x)*a;*/
13722 }
13723
13724 #if defined(MA_SUPPORT_SSE2)
13725 static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
13726 {
13727 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
13728 }
13729 #endif
13730 #if defined(MA_SUPPORT_AVX2)
13731 static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
13732 {
13733 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
13734 }
13735 #endif
13736 #if defined(MA_SUPPORT_NEON)
13737 static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
13738 {
13739 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
13740 }
13741 #endif
13742
13743
13744 static MA_INLINE double ma_mix_f64(double x, double y, double a)
13745 {
13746 return x*(1-a) + y*a;
13747 }
13748 static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
13749 {
13750 return x + (y - x)*a;
13751 }
13752
13753 static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
13754 {
13755 return lo + x*(hi-lo);
13756 }
13757
13758
13759 /*
13760 Greatest common factor using Euclid's algorithm iteratively.
13761 */
13762 static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
13763 {
13764 for (;;) {
13765 if (b == 0) {
13766 break;
13767 } else {
13768 ma_uint32 t = a;
13769 a = b;
13770 b = t % a;
13771 }
13772 }
13773
13774 return a;
13775 }
13776
13777
13778 static ma_uint32 ma_ffs_32(ma_uint32 x)
13779 {
13780 ma_uint32 i;
13781
13782 /* Just a naive implementation just to get things working for now. Will optimize this later. */
13783 for (i = 0; i < 32; i += 1) {
13784 if ((x & (1 << i)) != 0) {
13785 return i;
13786 }
13787 }
13788
13789 return i;
13790 }
13791
13792 static MA_INLINE ma_int16 ma_float_to_fixed_16(float x)
13793 {
13794 return (ma_int16)(x * (1 << 8));
13795 }
13796
13797
13798
13799 /*
13800 Random Number Generation
13801
13802 miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
13803
13804 Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across
13805 multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for
13806 miniaudio's purposes.
13807 */
13808 #ifndef MA_DEFAULT_LCG_SEED
13809 #define MA_DEFAULT_LCG_SEED 4321
13810 #endif
13811
13812 #define MA_LCG_M 2147483647
13813 #define MA_LCG_A 48271
13814 #define MA_LCG_C 0
13815
13816 static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
13817
13818 static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
13819 {
13820 MA_ASSERT(pLCG != NULL);
13821 pLCG->state = seed;
13822 }
13823
13824 static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
13825 {
13826 pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
13827 return pLCG->state;
13828 }
13829
13830 static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
13831 {
13832 return (ma_uint32)ma_lcg_rand_s32(pLCG);
13833 }
13834
13835 static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
13836 {
13837 return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
13838 }
13839
13840 static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
13841 {
13842 return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
13843 }
13844
13845 static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
13846 {
13847 return (float)ma_lcg_rand_f64(pLCG);
13848 }
13849
13850 static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
13851 {
13852 return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
13853 }
13854
13855 static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
13856 {
13857 if (lo == hi) {
13858 return lo;
13859 }
13860
13861 return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
13862 }
13863
13864
13865
13866 static MA_INLINE void ma_seed(ma_int32 seed)
13867 {
13868 ma_lcg_seed(&g_maLCG, seed);
13869 }
13870
13871 static MA_INLINE ma_int32 ma_rand_s32(void)
13872 {
13873 return ma_lcg_rand_s32(&g_maLCG);
13874 }
13875
13876 static MA_INLINE ma_uint32 ma_rand_u32(void)
13877 {
13878 return ma_lcg_rand_u32(&g_maLCG);
13879 }
13880
13881 static MA_INLINE double ma_rand_f64(void)
13882 {
13883 return ma_lcg_rand_f64(&g_maLCG);
13884 }
13885
13886 static MA_INLINE float ma_rand_f32(void)
13887 {
13888 return ma_lcg_rand_f32(&g_maLCG);
13889 }
13890
13891 static MA_INLINE float ma_rand_range_f32(float lo, float hi)
13892 {
13893 return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);
13894 }
13895
13896 static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
13897 {
13898 return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);
13899 }
13900
13901
13902 static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
13903 {
13904 return ma_rand_range_f32(ditherMin, ditherMax);
13905 }
13906
13907 static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
13908 {
13909 float a = ma_rand_range_f32(ditherMin, 0);
13910 float b = ma_rand_range_f32(0, ditherMax);
13911 return a + b;
13912 }
13913
13914 static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
13915 {
13916 if (ditherMode == ma_dither_mode_rectangle) {
13917 return ma_dither_f32_rectangle(ditherMin, ditherMax);
13918 }
13919 if (ditherMode == ma_dither_mode_triangle) {
13920 return ma_dither_f32_triangle(ditherMin, ditherMax);
13921 }
13922
13923 return 0;
13924 }
13925
13926 static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
13927 {
13928 if (ditherMode == ma_dither_mode_rectangle) {
13929 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
13930 return a;
13931 }
13932 if (ditherMode == ma_dither_mode_triangle) {
13933 ma_int32 a = ma_rand_range_s32(ditherMin, 0);
13934 ma_int32 b = ma_rand_range_s32(0, ditherMax);
13935 return a + b;
13936 }
13937
13938 return 0;
13939 }
13940
13941
13942 /**************************************************************************************************************************************************************
13943
13944 Atomics
13945
13946 **************************************************************************************************************************************************************/
13947 /* c89atomic.h begin */
13948 #ifndef c89atomic_h
13949 #define c89atomic_h
13950 #if defined(__cplusplus)
13951 extern "C" {
13952 #endif
13953 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
13954 #pragma GCC diagnostic push
13955 #pragma GCC diagnostic ignored "-Wlong-long"
13956 #if defined(__clang__)
13957 #pragma GCC diagnostic ignored "-Wc++11-long-long"
13958 #endif
13959 #endif
13960 typedef signed char c89atomic_int8;
13961 typedef unsigned char c89atomic_uint8;
13962 typedef signed short c89atomic_int16;
13963 typedef unsigned short c89atomic_uint16;
13964 typedef signed int c89atomic_int32;
13965 typedef unsigned int c89atomic_uint32;
13966 #if defined(_MSC_VER) && !defined(__clang__)
13967 typedef signed __int64 c89atomic_int64;
13968 typedef unsigned __int64 c89atomic_uint64;
13969 #else
13970 typedef signed long long c89atomic_int64;
13971 typedef unsigned long long c89atomic_uint64;
13972 #endif
13973 typedef int c89atomic_memory_order;
13974 typedef unsigned char c89atomic_bool;
13975 #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
13976 #ifdef _WIN32
13977 #ifdef _WIN64
13978 #define C89ATOMIC_64BIT
13979 #else
13980 #define C89ATOMIC_32BIT
13981 #endif
13982 #endif
13983 #endif
13984 #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
13985 #ifdef __GNUC__
13986 #ifdef __LP64__
13987 #define C89ATOMIC_64BIT
13988 #else
13989 #define C89ATOMIC_32BIT
13990 #endif
13991 #endif
13992 #endif
13993 #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
13994 #include <stdint.h>
13995 #if INTPTR_MAX == INT64_MAX
13996 #define C89ATOMIC_64BIT
13997 #else
13998 #define C89ATOMIC_32BIT
13999 #endif
14000 #endif
14001 #if defined(__arm__) || defined(_M_ARM)
14002 #define C89ATOMIC_ARM32
14003 #endif
14004 #if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
14005 #define C89ATOMIC_ARM64
14006 #endif
14007 #if defined(__x86_64__) || defined(_M_X64)
14008 #define C89ATOMIC_X64
14009 #elif defined(__i386) || defined(_M_IX86)
14010 #define C89ATOMIC_X86
14011 #elif defined(C89ATOMIC_ARM32) || defined(C89ATOMIC_ARM64)
14012 #define C89ATOMIC_ARM
14013 #endif
14014 #if defined(_MSC_VER)
14015 #define C89ATOMIC_INLINE __forceinline
14016 #elif defined(__GNUC__)
14017 #if defined(__STRICT_ANSI__)
14018 #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline))
14019 #else
14020 #define C89ATOMIC_INLINE inline __attribute__((always_inline))
14021 #endif
14022 #elif defined(__WATCOMC__) || defined(__DMC__)
14023 #define C89ATOMIC_INLINE __inline
14024 #else
14025 #define C89ATOMIC_INLINE
14026 #endif
14027 #define C89ATOMIC_HAS_8
14028 #define C89ATOMIC_HAS_16
14029 #define C89ATOMIC_HAS_32
14030 #define C89ATOMIC_HAS_64
14031 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__)
14032 #define C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, c89atomicType, msvcType) \
14033 c89atomicType result; \
14034 switch (order) \
14035 { \
14036 case c89atomic_memory_order_relaxed: \
14037 { \
14038 result = (c89atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \
14039 } break; \
14040 case c89atomic_memory_order_consume: \
14041 case c89atomic_memory_order_acquire: \
14042 { \
14043 result = (c89atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \
14044 } break; \
14045 case c89atomic_memory_order_release: \
14046 { \
14047 result = (c89atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \
14048 } break; \
14049 case c89atomic_memory_order_acq_rel: \
14050 case c89atomic_memory_order_seq_cst: \
14051 default: \
14052 { \
14053 result = (c89atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \
14054 } break; \
14055 } \
14056 return result;
14057 #define C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, c89atomicType, msvcType) \
14058 c89atomicType result; \
14059 switch (order) \
14060 { \
14061 case c89atomic_memory_order_relaxed: \
14062 { \
14063 result = (c89atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \
14064 } break; \
14065 case c89atomic_memory_order_consume: \
14066 case c89atomic_memory_order_acquire: \
14067 { \
14068 result = (c89atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \
14069 } break; \
14070 case c89atomic_memory_order_release: \
14071 { \
14072 result = (c89atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \
14073 } break; \
14074 case c89atomic_memory_order_acq_rel: \
14075 case c89atomic_memory_order_seq_cst: \
14076 default: \
14077 { \
14078 result = (c89atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \
14079 } break; \
14080 } \
14081 return result;
14082 #define c89atomic_memory_order_relaxed 0
14083 #define c89atomic_memory_order_consume 1
14084 #define c89atomic_memory_order_acquire 2
14085 #define c89atomic_memory_order_release 3
14086 #define c89atomic_memory_order_acq_rel 4
14087 #define c89atomic_memory_order_seq_cst 5
14088 #if _MSC_VER < 1600 && defined(C89ATOMIC_X86)
14089 #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY
14090 #endif
14091 #if _MSC_VER < 1600
14092 #undef C89ATOMIC_HAS_8
14093 #undef C89ATOMIC_HAS_16
14094 #endif
14095 #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
14096 #include <intrin.h>
14097 #endif
14098 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
14099 #if defined(C89ATOMIC_HAS_8)
14100 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
14101 {
14102 c89atomic_uint8 result = 0;
14103 __asm {
14104 mov ecx, dst
14105 mov al, expected
14106 mov dl, desired
14107 lock cmpxchg [ecx], dl
14108 mov result, al
14109 }
14110 return result;
14111 }
14112 #endif
14113 #if defined(C89ATOMIC_HAS_16)
14114 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
14115 {
14116 c89atomic_uint16 result = 0;
14117 __asm {
14118 mov ecx, dst
14119 mov ax, expected
14120 mov dx, desired
14121 lock cmpxchg [ecx], dx
14122 mov result, ax
14123 }
14124 return result;
14125 }
14126 #endif
14127 #if defined(C89ATOMIC_HAS_32)
14128 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
14129 {
14130 c89atomic_uint32 result = 0;
14131 __asm {
14132 mov ecx, dst
14133 mov eax, expected
14134 mov edx, desired
14135 lock cmpxchg [ecx], edx
14136 mov result, eax
14137 }
14138 return result;
14139 }
14140 #endif
14141 #if defined(C89ATOMIC_HAS_64)
14142 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
14143 {
14144 c89atomic_uint32 resultEAX = 0;
14145 c89atomic_uint32 resultEDX = 0;
14146 __asm {
14147 mov esi, dst
14148 mov eax, dword ptr expected
14149 mov edx, dword ptr expected + 4
14150 mov ebx, dword ptr desired
14151 mov ecx, dword ptr desired + 4
14152 lock cmpxchg8b qword ptr [esi]
14153 mov resultEAX, eax
14154 mov resultEDX, edx
14155 }
14156 return ((c89atomic_uint64)resultEDX << 32) | resultEAX;
14157 }
14158 #endif
14159 #else
14160 #if defined(C89ATOMIC_HAS_8)
14161 #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected)
14162 #endif
14163 #if defined(C89ATOMIC_HAS_16)
14164 #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected)
14165 #endif
14166 #if defined(C89ATOMIC_HAS_32)
14167 #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected)
14168 #endif
14169 #if defined(C89ATOMIC_HAS_64)
14170 #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected)
14171 #endif
14172 #endif
14173 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
14174 #if defined(C89ATOMIC_HAS_8)
14175 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14176 {
14177 c89atomic_uint8 result = 0;
14178 (void)order;
14179 __asm {
14180 mov ecx, dst
14181 mov al, src
14182 lock xchg [ecx], al
14183 mov result, al
14184 }
14185 return result;
14186 }
14187 #endif
14188 #if defined(C89ATOMIC_HAS_16)
14189 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14190 {
14191 c89atomic_uint16 result = 0;
14192 (void)order;
14193 __asm {
14194 mov ecx, dst
14195 mov ax, src
14196 lock xchg [ecx], ax
14197 mov result, ax
14198 }
14199 return result;
14200 }
14201 #endif
14202 #if defined(C89ATOMIC_HAS_32)
14203 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14204 {
14205 c89atomic_uint32 result = 0;
14206 (void)order;
14207 __asm {
14208 mov ecx, dst
14209 mov eax, src
14210 lock xchg [ecx], eax
14211 mov result, eax
14212 }
14213 return result;
14214 }
14215 #endif
14216 #else
14217 #if defined(C89ATOMIC_HAS_8)
14218 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14219 {
14220 #if defined(C89ATOMIC_ARM)
14221 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, c89atomic_uint8, char);
14222 #else
14223 (void)order;
14224 return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src);
14225 #endif
14226 }
14227 #endif
14228 #if defined(C89ATOMIC_HAS_16)
14229 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14230 {
14231 #if defined(C89ATOMIC_ARM)
14232 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, c89atomic_uint16, short);
14233 #else
14234 (void)order;
14235 return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src);
14236 #endif
14237 }
14238 #endif
14239 #if defined(C89ATOMIC_HAS_32)
14240 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14241 {
14242 #if defined(C89ATOMIC_ARM)
14243 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, c89atomic_uint32, long);
14244 #else
14245 (void)order;
14246 return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src);
14247 #endif
14248 }
14249 #endif
14250 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
14251 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14252 {
14253 #if defined(C89ATOMIC_ARM)
14254 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, c89atomic_uint64, long long);
14255 #else
14256 (void)order;
14257 return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src);
14258 #endif
14259 }
14260 #else
14261 #endif
14262 #endif
14263 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
14264 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14265 {
14266 c89atomic_uint64 oldValue;
14267 do {
14268 oldValue = *dst;
14269 } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue);
14270 (void)order;
14271 return oldValue;
14272 }
14273 #endif
14274 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
14275 #if defined(C89ATOMIC_HAS_8)
14276 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14277 {
14278 c89atomic_uint8 result = 0;
14279 (void)order;
14280 __asm {
14281 mov ecx, dst
14282 mov al, src
14283 lock xadd [ecx], al
14284 mov result, al
14285 }
14286 return result;
14287 }
14288 #endif
14289 #if defined(C89ATOMIC_HAS_16)
14290 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14291 {
14292 c89atomic_uint16 result = 0;
14293 (void)order;
14294 __asm {
14295 mov ecx, dst
14296 mov ax, src
14297 lock xadd [ecx], ax
14298 mov result, ax
14299 }
14300 return result;
14301 }
14302 #endif
14303 #if defined(C89ATOMIC_HAS_32)
14304 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14305 {
14306 c89atomic_uint32 result = 0;
14307 (void)order;
14308 __asm {
14309 mov ecx, dst
14310 mov eax, src
14311 lock xadd [ecx], eax
14312 mov result, eax
14313 }
14314 return result;
14315 }
14316 #endif
14317 #else
14318 #if defined(C89ATOMIC_HAS_8)
14319 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14320 {
14321 #if defined(C89ATOMIC_ARM)
14322 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, c89atomic_uint8, char);
14323 #else
14324 (void)order;
14325 return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);
14326 #endif
14327 }
14328 #endif
14329 #if defined(C89ATOMIC_HAS_16)
14330 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14331 {
14332 #if defined(C89ATOMIC_ARM)
14333 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, c89atomic_uint16, short);
14334 #else
14335 (void)order;
14336 return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);
14337 #endif
14338 }
14339 #endif
14340 #if defined(C89ATOMIC_HAS_32)
14341 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14342 {
14343 #if defined(C89ATOMIC_ARM)
14344 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, c89atomic_uint32, long);
14345 #else
14346 (void)order;
14347 return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);
14348 #endif
14349 }
14350 #endif
14351 #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT)
14352 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14353 {
14354 #if defined(C89ATOMIC_ARM)
14355 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, c89atomic_uint64, long long);
14356 #else
14357 (void)order;
14358 return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);
14359 #endif
14360 }
14361 #else
14362 #endif
14363 #endif
14364 #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT)
14365 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14366 {
14367 c89atomic_uint64 oldValue;
14368 c89atomic_uint64 newValue;
14369 do {
14370 oldValue = *dst;
14371 newValue = oldValue + src;
14372 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14373 (void)order;
14374 return oldValue;
14375 }
14376 #endif
14377 #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY)
14378 static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order)
14379 {
14380 (void)order;
14381 __asm {
14382 lock add [esp], 0
14383 }
14384 }
14385 #else
14386 #if defined(C89ATOMIC_X64)
14387 #define c89atomic_thread_fence(order) __faststorefence(), (void)order
14388 #elif defined(C89ATOMIC_ARM64)
14389 #define c89atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order
14390 #else
14391 static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order)
14392 {
14393 volatile c89atomic_uint32 barrier = 0;
14394 c89atomic_fetch_add_explicit_32(&barrier, 0, order);
14395 }
14396 #endif
14397 #endif
14398 #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst)
14399 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
14400 #if defined(C89ATOMIC_HAS_8)
14401 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
14402 {
14403 #if defined(C89ATOMIC_ARM)
14404 C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, c89atomic_uint8, char);
14405 #else
14406 (void)order;
14407 return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0);
14408 #endif
14409 }
14410 #endif
14411 #if defined(C89ATOMIC_HAS_16)
14412 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
14413 {
14414 #if defined(C89ATOMIC_ARM)
14415 C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, c89atomic_uint16, short);
14416 #else
14417 (void)order;
14418 return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0);
14419 #endif
14420 }
14421 #endif
14422 #if defined(C89ATOMIC_HAS_32)
14423 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
14424 {
14425 #if defined(C89ATOMIC_ARM)
14426 C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, c89atomic_uint32, long);
14427 #else
14428 (void)order;
14429 return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0);
14430 #endif
14431 }
14432 #endif
14433 #if defined(C89ATOMIC_HAS_64)
14434 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
14435 {
14436 #if defined(C89ATOMIC_ARM)
14437 C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, c89atomic_uint64, long long);
14438 #else
14439 (void)order;
14440 return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0);
14441 #endif
14442 }
14443 #endif
14444 #if defined(C89ATOMIC_HAS_8)
14445 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
14446 #endif
14447 #if defined(C89ATOMIC_HAS_16)
14448 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
14449 #endif
14450 #if defined(C89ATOMIC_HAS_32)
14451 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
14452 #endif
14453 #if defined(C89ATOMIC_HAS_64)
14454 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
14455 #endif
14456 #if defined(C89ATOMIC_HAS_8)
14457 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14458 {
14459 c89atomic_uint8 oldValue;
14460 c89atomic_uint8 newValue;
14461 do {
14462 oldValue = *dst;
14463 newValue = (c89atomic_uint8)(oldValue - src);
14464 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14465 (void)order;
14466 return oldValue;
14467 }
14468 #endif
14469 #if defined(C89ATOMIC_HAS_16)
14470 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14471 {
14472 c89atomic_uint16 oldValue;
14473 c89atomic_uint16 newValue;
14474 do {
14475 oldValue = *dst;
14476 newValue = (c89atomic_uint16)(oldValue - src);
14477 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14478 (void)order;
14479 return oldValue;
14480 }
14481 #endif
14482 #if defined(C89ATOMIC_HAS_32)
14483 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14484 {
14485 c89atomic_uint32 oldValue;
14486 c89atomic_uint32 newValue;
14487 do {
14488 oldValue = *dst;
14489 newValue = oldValue - src;
14490 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14491 (void)order;
14492 return oldValue;
14493 }
14494 #endif
14495 #if defined(C89ATOMIC_HAS_64)
14496 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14497 {
14498 c89atomic_uint64 oldValue;
14499 c89atomic_uint64 newValue;
14500 do {
14501 oldValue = *dst;
14502 newValue = oldValue - src;
14503 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14504 (void)order;
14505 return oldValue;
14506 }
14507 #endif
14508 #if defined(C89ATOMIC_HAS_8)
14509 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14510 {
14511 #if defined(C89ATOMIC_ARM)
14512 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, c89atomic_uint8, char);
14513 #else
14514 c89atomic_uint8 oldValue;
14515 c89atomic_uint8 newValue;
14516 do {
14517 oldValue = *dst;
14518 newValue = (c89atomic_uint8)(oldValue & src);
14519 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14520 (void)order;
14521 return oldValue;
14522 #endif
14523 }
14524 #endif
14525 #if defined(C89ATOMIC_HAS_16)
14526 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14527 {
14528 #if defined(C89ATOMIC_ARM)
14529 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, c89atomic_uint16, short);
14530 #else
14531 c89atomic_uint16 oldValue;
14532 c89atomic_uint16 newValue;
14533 do {
14534 oldValue = *dst;
14535 newValue = (c89atomic_uint16)(oldValue & src);
14536 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14537 (void)order;
14538 return oldValue;
14539 #endif
14540 }
14541 #endif
14542 #if defined(C89ATOMIC_HAS_32)
14543 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14544 {
14545 #if defined(C89ATOMIC_ARM)
14546 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, c89atomic_uint32, long);
14547 #else
14548 c89atomic_uint32 oldValue;
14549 c89atomic_uint32 newValue;
14550 do {
14551 oldValue = *dst;
14552 newValue = oldValue & src;
14553 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14554 (void)order;
14555 return oldValue;
14556 #endif
14557 }
14558 #endif
14559 #if defined(C89ATOMIC_HAS_64)
14560 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14561 {
14562 #if defined(C89ATOMIC_ARM)
14563 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, c89atomic_uint64, long long);
14564 #else
14565 c89atomic_uint64 oldValue;
14566 c89atomic_uint64 newValue;
14567 do {
14568 oldValue = *dst;
14569 newValue = oldValue & src;
14570 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14571 (void)order;
14572 return oldValue;
14573 #endif
14574 }
14575 #endif
14576 #if defined(C89ATOMIC_HAS_8)
14577 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14578 {
14579 #if defined(C89ATOMIC_ARM)
14580 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, c89atomic_uint8, char);
14581 #else
14582 c89atomic_uint8 oldValue;
14583 c89atomic_uint8 newValue;
14584 do {
14585 oldValue = *dst;
14586 newValue = (c89atomic_uint8)(oldValue ^ src);
14587 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14588 (void)order;
14589 return oldValue;
14590 #endif
14591 }
14592 #endif
14593 #if defined(C89ATOMIC_HAS_16)
14594 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14595 {
14596 #if defined(C89ATOMIC_ARM)
14597 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, c89atomic_uint16, short);
14598 #else
14599 c89atomic_uint16 oldValue;
14600 c89atomic_uint16 newValue;
14601 do {
14602 oldValue = *dst;
14603 newValue = (c89atomic_uint16)(oldValue ^ src);
14604 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14605 (void)order;
14606 return oldValue;
14607 #endif
14608 }
14609 #endif
14610 #if defined(C89ATOMIC_HAS_32)
14611 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14612 {
14613 #if defined(C89ATOMIC_ARM)
14614 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, c89atomic_uint32, long);
14615 #else
14616 c89atomic_uint32 oldValue;
14617 c89atomic_uint32 newValue;
14618 do {
14619 oldValue = *dst;
14620 newValue = oldValue ^ src;
14621 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14622 (void)order;
14623 return oldValue;
14624 #endif
14625 }
14626 #endif
14627 #if defined(C89ATOMIC_HAS_64)
14628 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14629 {
14630 #if defined(C89ATOMIC_ARM)
14631 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, c89atomic_uint64, long long);
14632 #else
14633 c89atomic_uint64 oldValue;
14634 c89atomic_uint64 newValue;
14635 do {
14636 oldValue = *dst;
14637 newValue = oldValue ^ src;
14638 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14639 (void)order;
14640 return oldValue;
14641 #endif
14642 }
14643 #endif
14644 #if defined(C89ATOMIC_HAS_8)
14645 static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14646 {
14647 #if defined(C89ATOMIC_ARM)
14648 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, c89atomic_uint8, char);
14649 #else
14650 c89atomic_uint8 oldValue;
14651 c89atomic_uint8 newValue;
14652 do {
14653 oldValue = *dst;
14654 newValue = (c89atomic_uint8)(oldValue | src);
14655 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
14656 (void)order;
14657 return oldValue;
14658 #endif
14659 }
14660 #endif
14661 #if defined(C89ATOMIC_HAS_16)
14662 static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14663 {
14664 #if defined(C89ATOMIC_ARM)
14665 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, c89atomic_uint16, short);
14666 #else
14667 c89atomic_uint16 oldValue;
14668 c89atomic_uint16 newValue;
14669 do {
14670 oldValue = *dst;
14671 newValue = (c89atomic_uint16)(oldValue | src);
14672 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
14673 (void)order;
14674 return oldValue;
14675 #endif
14676 }
14677 #endif
14678 #if defined(C89ATOMIC_HAS_32)
14679 static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14680 {
14681 #if defined(C89ATOMIC_ARM)
14682 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, c89atomic_uint32, long);
14683 #else
14684 c89atomic_uint32 oldValue;
14685 c89atomic_uint32 newValue;
14686 do {
14687 oldValue = *dst;
14688 newValue = oldValue | src;
14689 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
14690 (void)order;
14691 return oldValue;
14692 #endif
14693 }
14694 #endif
14695 #if defined(C89ATOMIC_HAS_64)
14696 static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14697 {
14698 #if defined(C89ATOMIC_ARM)
14699 C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, c89atomic_uint64, long long);
14700 #else
14701 c89atomic_uint64 oldValue;
14702 c89atomic_uint64 newValue;
14703 do {
14704 oldValue = *dst;
14705 newValue = oldValue | src;
14706 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
14707 (void)order;
14708 return oldValue;
14709 #endif
14710 }
14711 #endif
14712 #if defined(C89ATOMIC_HAS_8)
14713 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
14714 #endif
14715 #if defined(C89ATOMIC_HAS_16)
14716 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
14717 #endif
14718 #if defined(C89ATOMIC_HAS_32)
14719 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
14720 #endif
14721 #if defined(C89ATOMIC_HAS_64)
14722 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
14723 #endif
14724 #if defined(C89ATOMIC_HAS_8)
14725 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
14726 #endif
14727 #if defined(C89ATOMIC_HAS_16)
14728 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
14729 #endif
14730 #if defined(C89ATOMIC_HAS_32)
14731 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
14732 #endif
14733 #if defined(C89ATOMIC_HAS_64)
14734 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
14735 #endif
14736 #if defined(C89ATOMIC_HAS_8)
14737 typedef c89atomic_uint8 c89atomic_flag;
14738 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
14739 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
14740 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
14741 #else
14742 typedef c89atomic_uint32 c89atomic_flag;
14743 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order)
14744 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order)
14745 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order)
14746 #endif
14747 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
14748 #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
14749 #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE
14750 #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED
14751 #define c89atomic_memory_order_consume __ATOMIC_CONSUME
14752 #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE
14753 #define c89atomic_memory_order_release __ATOMIC_RELEASE
14754 #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL
14755 #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST
14756 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
14757 #define c89atomic_thread_fence(order) __atomic_thread_fence(order)
14758 #define c89atomic_signal_fence(order) __atomic_signal_fence(order)
14759 #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr)
14760 #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr)
14761 #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr)
14762 #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr)
14763 #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order)
14764 #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order)
14765 #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order)
14766 #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order)
14767 #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order)
14768 #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order)
14769 #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order)
14770 #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order)
14771 #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order)
14772 #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order)
14773 #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order)
14774 #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order)
14775 #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order)
14776 #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order)
14777 #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order)
14778 #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order)
14779 #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order)
14780 #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order)
14781 #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order)
14782 #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order)
14783 #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14784 #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14785 #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14786 #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder)
14787 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14788 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14789 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14790 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder)
14791 #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order)
14792 #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order)
14793 #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order)
14794 #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order)
14795 #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order)
14796 #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order)
14797 #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order)
14798 #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order)
14799 #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order)
14800 #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order)
14801 #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order)
14802 #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order)
14803 #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order)
14804 #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order)
14805 #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order)
14806 #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order)
14807 #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order)
14808 #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order)
14809 #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order)
14810 #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order)
14811 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
14812 {
14813 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
14814 return expected;
14815 }
14816 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
14817 {
14818 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
14819 return expected;
14820 }
14821 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
14822 {
14823 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
14824 return expected;
14825 }
14826 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
14827 {
14828 __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
14829 return expected;
14830 }
14831 typedef c89atomic_uint8 c89atomic_flag;
14832 #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order)
14833 #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order)
14834 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
14835 #else
14836 #define c89atomic_memory_order_relaxed 1
14837 #define c89atomic_memory_order_consume 2
14838 #define c89atomic_memory_order_acquire 3
14839 #define c89atomic_memory_order_release 4
14840 #define c89atomic_memory_order_acq_rel 5
14841 #define c89atomic_memory_order_seq_cst 6
14842 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory")
14843 #if defined(__GNUC__)
14844 #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order
14845 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14846 {
14847 if (order > c89atomic_memory_order_acquire) {
14848 __sync_synchronize();
14849 }
14850 return __sync_lock_test_and_set(dst, src);
14851 }
14852 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14853 {
14854 c89atomic_uint16 oldValue;
14855 do {
14856 oldValue = *dst;
14857 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
14858 (void)order;
14859 return oldValue;
14860 }
14861 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14862 {
14863 c89atomic_uint32 oldValue;
14864 do {
14865 oldValue = *dst;
14866 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
14867 (void)order;
14868 return oldValue;
14869 }
14870 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14871 {
14872 c89atomic_uint64 oldValue;
14873 do {
14874 oldValue = *dst;
14875 } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue);
14876 (void)order;
14877 return oldValue;
14878 }
14879 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14880 {
14881 (void)order;
14882 return __sync_fetch_and_add(dst, src);
14883 }
14884 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14885 {
14886 (void)order;
14887 return __sync_fetch_and_add(dst, src);
14888 }
14889 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14890 {
14891 (void)order;
14892 return __sync_fetch_and_add(dst, src);
14893 }
14894 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14895 {
14896 (void)order;
14897 return __sync_fetch_and_add(dst, src);
14898 }
14899 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14900 {
14901 (void)order;
14902 return __sync_fetch_and_sub(dst, src);
14903 }
14904 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14905 {
14906 (void)order;
14907 return __sync_fetch_and_sub(dst, src);
14908 }
14909 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14910 {
14911 (void)order;
14912 return __sync_fetch_and_sub(dst, src);
14913 }
14914 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14915 {
14916 (void)order;
14917 return __sync_fetch_and_sub(dst, src);
14918 }
14919 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14920 {
14921 (void)order;
14922 return __sync_fetch_and_or(dst, src);
14923 }
14924 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14925 {
14926 (void)order;
14927 return __sync_fetch_and_or(dst, src);
14928 }
14929 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14930 {
14931 (void)order;
14932 return __sync_fetch_and_or(dst, src);
14933 }
14934 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14935 {
14936 (void)order;
14937 return __sync_fetch_and_or(dst, src);
14938 }
14939 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14940 {
14941 (void)order;
14942 return __sync_fetch_and_xor(dst, src);
14943 }
14944 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14945 {
14946 (void)order;
14947 return __sync_fetch_and_xor(dst, src);
14948 }
14949 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14950 {
14951 (void)order;
14952 return __sync_fetch_and_xor(dst, src);
14953 }
14954 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14955 {
14956 (void)order;
14957 return __sync_fetch_and_xor(dst, src);
14958 }
14959 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
14960 {
14961 (void)order;
14962 return __sync_fetch_and_and(dst, src);
14963 }
14964 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
14965 {
14966 (void)order;
14967 return __sync_fetch_and_and(dst, src);
14968 }
14969 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
14970 {
14971 (void)order;
14972 return __sync_fetch_and_and(dst, src);
14973 }
14974 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
14975 {
14976 (void)order;
14977 return __sync_fetch_and_and(dst, src);
14978 }
14979 #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14980 #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14981 #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14982 #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired)
14983 #else
14984 #if defined(C89ATOMIC_X86)
14985 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc")
14986 #elif defined(C89ATOMIC_X64)
14987 #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc")
14988 #else
14989 #error Unsupported architecture. Please submit a feature request.
14990 #endif
14991 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired)
14992 {
14993 c89atomic_uint8 result;
14994 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
14995 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
14996 #else
14997 #error Unsupported architecture. Please submit a feature request.
14998 #endif
14999 return result;
15000 }
15001 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired)
15002 {
15003 c89atomic_uint16 result;
15004 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15005 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
15006 #else
15007 #error Unsupported architecture. Please submit a feature request.
15008 #endif
15009 return result;
15010 }
15011 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired)
15012 {
15013 c89atomic_uint32 result;
15014 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15015 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
15016 #else
15017 #error Unsupported architecture. Please submit a feature request.
15018 #endif
15019 return result;
15020 }
15021 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired)
15022 {
15023 volatile c89atomic_uint64 result;
15024 #if defined(C89ATOMIC_X86)
15025 c89atomic_uint32 resultEAX;
15026 c89atomic_uint32 resultEDX;
15027 __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");
15028 result = ((c89atomic_uint64)resultEDX << 32) | resultEAX;
15029 #elif defined(C89ATOMIC_X64)
15030 __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc");
15031 #else
15032 #error Unsupported architecture. Please submit a feature request.
15033 #endif
15034 return result;
15035 }
15036 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
15037 {
15038 c89atomic_uint8 result = 0;
15039 (void)order;
15040 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15041 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
15042 #else
15043 #error Unsupported architecture. Please submit a feature request.
15044 #endif
15045 return result;
15046 }
15047 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
15048 {
15049 c89atomic_uint16 result = 0;
15050 (void)order;
15051 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15052 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
15053 #else
15054 #error Unsupported architecture. Please submit a feature request.
15055 #endif
15056 return result;
15057 }
15058 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
15059 {
15060 c89atomic_uint32 result;
15061 (void)order;
15062 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15063 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
15064 #else
15065 #error Unsupported architecture. Please submit a feature request.
15066 #endif
15067 return result;
15068 }
15069 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
15070 {
15071 c89atomic_uint64 result;
15072 (void)order;
15073 #if defined(C89ATOMIC_X86)
15074 do {
15075 result = *dst;
15076 } while (c89atomic_compare_and_swap_64(dst, result, src) != result);
15077 #elif defined(C89ATOMIC_X64)
15078 __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src));
15079 #else
15080 #error Unsupported architecture. Please submit a feature request.
15081 #endif
15082 return result;
15083 }
15084 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
15085 {
15086 c89atomic_uint8 result;
15087 (void)order;
15088 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15089 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
15090 #else
15091 #error Unsupported architecture. Please submit a feature request.
15092 #endif
15093 return result;
15094 }
15095 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
15096 {
15097 c89atomic_uint16 result;
15098 (void)order;
15099 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15100 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
15101 #else
15102 #error Unsupported architecture. Please submit a feature request.
15103 #endif
15104 return result;
15105 }
15106 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
15107 {
15108 c89atomic_uint32 result;
15109 (void)order;
15110 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15111 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
15112 #else
15113 #error Unsupported architecture. Please submit a feature request.
15114 #endif
15115 return result;
15116 }
15117 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
15118 {
15119 #if defined(C89ATOMIC_X86)
15120 c89atomic_uint64 oldValue;
15121 c89atomic_uint64 newValue;
15122 (void)order;
15123 do {
15124 oldValue = *dst;
15125 newValue = oldValue + src;
15126 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
15127 return oldValue;
15128 #elif defined(C89ATOMIC_X64)
15129 c89atomic_uint64 result;
15130 (void)order;
15131 __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc");
15132 return result;
15133 #endif
15134 }
15135 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
15136 {
15137 c89atomic_uint8 oldValue;
15138 c89atomic_uint8 newValue;
15139 do {
15140 oldValue = *dst;
15141 newValue = (c89atomic_uint8)(oldValue - src);
15142 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
15143 (void)order;
15144 return oldValue;
15145 }
15146 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
15147 {
15148 c89atomic_uint16 oldValue;
15149 c89atomic_uint16 newValue;
15150 do {
15151 oldValue = *dst;
15152 newValue = (c89atomic_uint16)(oldValue - src);
15153 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
15154 (void)order;
15155 return oldValue;
15156 }
15157 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
15158 {
15159 c89atomic_uint32 oldValue;
15160 c89atomic_uint32 newValue;
15161 do {
15162 oldValue = *dst;
15163 newValue = oldValue - src;
15164 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
15165 (void)order;
15166 return oldValue;
15167 }
15168 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
15169 {
15170 c89atomic_uint64 oldValue;
15171 c89atomic_uint64 newValue;
15172 do {
15173 oldValue = *dst;
15174 newValue = oldValue - src;
15175 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
15176 (void)order;
15177 return oldValue;
15178 }
15179 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
15180 {
15181 c89atomic_uint8 oldValue;
15182 c89atomic_uint8 newValue;
15183 do {
15184 oldValue = *dst;
15185 newValue = (c89atomic_uint8)(oldValue & src);
15186 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
15187 (void)order;
15188 return oldValue;
15189 }
15190 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
15191 {
15192 c89atomic_uint16 oldValue;
15193 c89atomic_uint16 newValue;
15194 do {
15195 oldValue = *dst;
15196 newValue = (c89atomic_uint16)(oldValue & src);
15197 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
15198 (void)order;
15199 return oldValue;
15200 }
15201 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
15202 {
15203 c89atomic_uint32 oldValue;
15204 c89atomic_uint32 newValue;
15205 do {
15206 oldValue = *dst;
15207 newValue = oldValue & src;
15208 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
15209 (void)order;
15210 return oldValue;
15211 }
15212 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
15213 {
15214 c89atomic_uint64 oldValue;
15215 c89atomic_uint64 newValue;
15216 do {
15217 oldValue = *dst;
15218 newValue = oldValue & src;
15219 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
15220 (void)order;
15221 return oldValue;
15222 }
15223 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
15224 {
15225 c89atomic_uint8 oldValue;
15226 c89atomic_uint8 newValue;
15227 do {
15228 oldValue = *dst;
15229 newValue = (c89atomic_uint8)(oldValue ^ src);
15230 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
15231 (void)order;
15232 return oldValue;
15233 }
15234 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
15235 {
15236 c89atomic_uint16 oldValue;
15237 c89atomic_uint16 newValue;
15238 do {
15239 oldValue = *dst;
15240 newValue = (c89atomic_uint16)(oldValue ^ src);
15241 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
15242 (void)order;
15243 return oldValue;
15244 }
15245 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
15246 {
15247 c89atomic_uint32 oldValue;
15248 c89atomic_uint32 newValue;
15249 do {
15250 oldValue = *dst;
15251 newValue = oldValue ^ src;
15252 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
15253 (void)order;
15254 return oldValue;
15255 }
15256 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
15257 {
15258 c89atomic_uint64 oldValue;
15259 c89atomic_uint64 newValue;
15260 do {
15261 oldValue = *dst;
15262 newValue = oldValue ^ src;
15263 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
15264 (void)order;
15265 return oldValue;
15266 }
15267 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order)
15268 {
15269 c89atomic_uint8 oldValue;
15270 c89atomic_uint8 newValue;
15271 do {
15272 oldValue = *dst;
15273 newValue = (c89atomic_uint8)(oldValue | src);
15274 } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue);
15275 (void)order;
15276 return oldValue;
15277 }
15278 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order)
15279 {
15280 c89atomic_uint16 oldValue;
15281 c89atomic_uint16 newValue;
15282 do {
15283 oldValue = *dst;
15284 newValue = (c89atomic_uint16)(oldValue | src);
15285 } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue);
15286 (void)order;
15287 return oldValue;
15288 }
15289 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order)
15290 {
15291 c89atomic_uint32 oldValue;
15292 c89atomic_uint32 newValue;
15293 do {
15294 oldValue = *dst;
15295 newValue = oldValue | src;
15296 } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue);
15297 (void)order;
15298 return oldValue;
15299 }
15300 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order)
15301 {
15302 c89atomic_uint64 oldValue;
15303 c89atomic_uint64 newValue;
15304 do {
15305 oldValue = *dst;
15306 newValue = oldValue | src;
15307 } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue);
15308 (void)order;
15309 return oldValue;
15310 }
15311 #endif
15312 #define c89atomic_signal_fence(order) c89atomic_thread_fence(order)
15313 static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order)
15314 {
15315 (void)order;
15316 return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0);
15317 }
15318 static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order)
15319 {
15320 (void)order;
15321 return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0);
15322 }
15323 static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order)
15324 {
15325 (void)order;
15326 return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0);
15327 }
15328 static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order)
15329 {
15330 (void)order;
15331 return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0);
15332 }
15333 #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order)
15334 #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order)
15335 #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order)
15336 #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order)
15337 #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order)
15338 #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order)
15339 #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order)
15340 #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order)
15341 #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order)
15342 #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order)
15343 #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order)
15344 #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order)
15345 typedef c89atomic_uint8 c89atomic_flag;
15346 #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order)
15347 #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order)
15348 #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order)
15349 #endif
15350 #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
15351 #if defined(C89ATOMIC_HAS_8)
15352 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15353 {
15354 c89atomic_uint8 expectedValue;
15355 c89atomic_uint8 result;
15356 (void)successOrder;
15357 (void)failureOrder;
15358 expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst);
15359 result = c89atomic_compare_and_swap_8(dst, expectedValue, desired);
15360 if (result == expectedValue) {
15361 return 1;
15362 } else {
15363 c89atomic_store_explicit_8(expected, result, failureOrder);
15364 return 0;
15365 }
15366 }
15367 #endif
15368 #if defined(C89ATOMIC_HAS_16)
15369 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15370 {
15371 c89atomic_uint16 expectedValue;
15372 c89atomic_uint16 result;
15373 (void)successOrder;
15374 (void)failureOrder;
15375 expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst);
15376 result = c89atomic_compare_and_swap_16(dst, expectedValue, desired);
15377 if (result == expectedValue) {
15378 return 1;
15379 } else {
15380 c89atomic_store_explicit_16(expected, result, failureOrder);
15381 return 0;
15382 }
15383 }
15384 #endif
15385 #if defined(C89ATOMIC_HAS_32)
15386 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15387 {
15388 c89atomic_uint32 expectedValue;
15389 c89atomic_uint32 result;
15390 (void)successOrder;
15391 (void)failureOrder;
15392 expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst);
15393 result = c89atomic_compare_and_swap_32(dst, expectedValue, desired);
15394 if (result == expectedValue) {
15395 return 1;
15396 } else {
15397 c89atomic_store_explicit_32(expected, result, failureOrder);
15398 return 0;
15399 }
15400 }
15401 #endif
15402 #if defined(C89ATOMIC_HAS_64)
15403 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15404 {
15405 c89atomic_uint64 expectedValue;
15406 c89atomic_uint64 result;
15407 (void)successOrder;
15408 (void)failureOrder;
15409 expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst);
15410 result = c89atomic_compare_and_swap_64(dst, expectedValue, desired);
15411 if (result == expectedValue) {
15412 return 1;
15413 } else {
15414 c89atomic_store_explicit_64(expected, result, failureOrder);
15415 return 0;
15416 }
15417 }
15418 #endif
15419 #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder)
15420 #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder)
15421 #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder)
15422 #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder)
15423 #endif
15424 #if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE)
15425 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr)
15426 {
15427 (void)ptr;
15428 return 1;
15429 }
15430 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr)
15431 {
15432 (void)ptr;
15433 return 1;
15434 }
15435 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr)
15436 {
15437 (void)ptr;
15438 return 1;
15439 }
15440 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr)
15441 {
15442 (void)ptr;
15443 #if defined(C89ATOMIC_64BIT)
15444 return 1;
15445 #else
15446 #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64)
15447 return 1;
15448 #else
15449 return 0;
15450 #endif
15451 #endif
15452 }
15453 #endif
15454 #if defined(C89ATOMIC_64BIT)
15455 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
15456 {
15457 return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr);
15458 }
15459 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
15460 {
15461 return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order);
15462 }
15463 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
15464 {
15465 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
15466 }
15467 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
15468 {
15469 return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order);
15470 }
15471 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15472 {
15473 return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
15474 }
15475 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15476 {
15477 return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder);
15478 }
15479 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
15480 {
15481 return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired);
15482 }
15483 #elif defined(C89ATOMIC_32BIT)
15484 static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr)
15485 {
15486 return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr);
15487 }
15488 static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order)
15489 {
15490 return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order);
15491 }
15492 static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
15493 {
15494 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
15495 }
15496 static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order)
15497 {
15498 return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order);
15499 }
15500 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15501 {
15502 return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
15503 }
15504 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15505 {
15506 return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder);
15507 }
15508 static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired)
15509 {
15510 return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired);
15511 }
15512 #else
15513 #error Unsupported architecture.
15514 #endif
15515 #define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst)
15516 #define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst)
15517 #define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
15518 #define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst)
15519 #define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst)
15520 #define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15521 #define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15522 #define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst)
15523 #define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst)
15524 #define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst)
15525 #define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst)
15526 #define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst)
15527 #define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst)
15528 #define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst)
15529 #define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst)
15530 #define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
15531 #define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
15532 #define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
15533 #define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15534 #define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst)
15535 #define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst)
15536 #define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst)
15537 #define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst)
15538 #define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
15539 #define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
15540 #define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
15541 #define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15542 #define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15543 #define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15544 #define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15545 #define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15546 #define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15547 #define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15548 #define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15549 #define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15550 #define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
15551 #define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
15552 #define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
15553 #define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15554 #define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
15555 #define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
15556 #define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
15557 #define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15558 #define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
15559 #define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
15560 #define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
15561 #define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15562 #define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst)
15563 #define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
15564 #define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
15565 #define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15566 #define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst)
15567 #define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst)
15568 #define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst)
15569 #define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst)
15570 #define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order)
15571 #define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order)
15572 #define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order)
15573 #define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order)
15574 #define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order)
15575 #define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order)
15576 #define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
15577 #define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
15578 #define c89atomic_store_explicit_i8( dst, src, order) c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15579 #define c89atomic_store_explicit_i16(dst, src, order) c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15580 #define c89atomic_store_explicit_i32(dst, src, order) c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15581 #define c89atomic_store_explicit_i64(dst, src, order) c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15582 #define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order)
15583 #define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order)
15584 #define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order)
15585 #define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order)
15586 #define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15587 #define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15588 #define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15589 #define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15590 #define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder)
15591 #define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder)
15592 #define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder)
15593 #define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder)
15594 #define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder)
15595 #define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder)
15596 #define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder)
15597 #define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder)
15598 #define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15599 #define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15600 #define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15601 #define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15602 #define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15603 #define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15604 #define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15605 #define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15606 #define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15607 #define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15608 #define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15609 #define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15610 #define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15611 #define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15612 #define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15613 #define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15614 #define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order)
15615 #define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order)
15616 #define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order)
15617 #define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order)
15618 #define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
15619 #define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
15620 #define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
15621 #define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
15622 #define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
15623 #define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
15624 #define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
15625 #define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
15626 #define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15627 #define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15628 #define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15629 #define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15630 #define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst)
15631 #define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst)
15632 #define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst)
15633 #define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst)
15634 #define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15635 #define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15636 #define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15637 #define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15638 #define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15639 #define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15640 #define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15641 #define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15642 #define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15643 #define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15644 #define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15645 #define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15646 #define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15647 #define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15648 #define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15649 #define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15650 #define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15651 #define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15652 #define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15653 #define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15654 #define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15655 #define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15656 #define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15657 #define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15658 #define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15659 #define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15660 #define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15661 #define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15662 #define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst)
15663 #define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst)
15664 #define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst)
15665 #define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst)
15666 #define c89atomic_compare_and_swap_i8( dst, expected, dedsired) (c89atomic_int8 )c89atomic_compare_and_swap_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )expected, (c89atomic_uint8 )dedsired)
15667 #define c89atomic_compare_and_swap_i16(dst, expected, dedsired) (c89atomic_int16)c89atomic_compare_and_swap_16((c89atomic_uint16*)dst, (c89atomic_uint16)expected, (c89atomic_uint16)dedsired)
15668 #define c89atomic_compare_and_swap_i32(dst, expected, dedsired) (c89atomic_int32)c89atomic_compare_and_swap_32((c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)dedsired)
15669 #define c89atomic_compare_and_swap_i64(dst, expected, dedsired) (c89atomic_int64)c89atomic_compare_and_swap_64((c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)dedsired)
15670 typedef union
15671 {
15672 c89atomic_uint32 i;
15673 float f;
15674 } c89atomic_if32;
15675 typedef union
15676 {
15677 c89atomic_uint64 i;
15678 double f;
15679 } c89atomic_if64;
15680 #define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order)
15681 #define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order)
15682 static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15683 {
15684 c89atomic_if32 x;
15685 x.f = src;
15686 c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15687 }
15688 static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15689 {
15690 c89atomic_if64 x;
15691 x.f = src;
15692 c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15693 }
15694 static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order)
15695 {
15696 c89atomic_if32 r;
15697 r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order);
15698 return r.f;
15699 }
15700 static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order)
15701 {
15702 c89atomic_if64 r;
15703 r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order);
15704 return r.f;
15705 }
15706 static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15707 {
15708 c89atomic_if32 r;
15709 c89atomic_if32 x;
15710 x.f = src;
15711 r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15712 return r.f;
15713 }
15714 static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15715 {
15716 c89atomic_if64 r;
15717 c89atomic_if64 x;
15718 x.f = src;
15719 r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15720 return r.f;
15721 }
15722 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15723 {
15724 c89atomic_if32 d;
15725 d.f = desired;
15726 return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder);
15727 }
15728 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15729 {
15730 c89atomic_if64 d;
15731 d.f = desired;
15732 return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder);
15733 }
15734 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15735 {
15736 c89atomic_if32 d;
15737 d.f = desired;
15738 return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder);
15739 }
15740 static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder)
15741 {
15742 c89atomic_if64 d;
15743 d.f = desired;
15744 return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder);
15745 }
15746 static C89ATOMIC_INLINE float c89atomic_fetch_add_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15747 {
15748 c89atomic_if32 r;
15749 c89atomic_if32 x;
15750 x.f = src;
15751 r.i = c89atomic_fetch_add_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15752 return r.f;
15753 }
15754 static C89ATOMIC_INLINE double c89atomic_fetch_add_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15755 {
15756 c89atomic_if64 r;
15757 c89atomic_if64 x;
15758 x.f = src;
15759 r.i = c89atomic_fetch_add_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15760 return r.f;
15761 }
15762 static C89ATOMIC_INLINE float c89atomic_fetch_sub_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15763 {
15764 c89atomic_if32 r;
15765 c89atomic_if32 x;
15766 x.f = src;
15767 r.i = c89atomic_fetch_sub_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15768 return r.f;
15769 }
15770 static C89ATOMIC_INLINE double c89atomic_fetch_sub_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15771 {
15772 c89atomic_if64 r;
15773 c89atomic_if64 x;
15774 x.f = src;
15775 r.i = c89atomic_fetch_sub_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15776 return r.f;
15777 }
15778 static C89ATOMIC_INLINE float c89atomic_fetch_or_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15779 {
15780 c89atomic_if32 r;
15781 c89atomic_if32 x;
15782 x.f = src;
15783 r.i = c89atomic_fetch_or_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15784 return r.f;
15785 }
15786 static C89ATOMIC_INLINE double c89atomic_fetch_or_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15787 {
15788 c89atomic_if64 r;
15789 c89atomic_if64 x;
15790 x.f = src;
15791 r.i = c89atomic_fetch_or_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15792 return r.f;
15793 }
15794 static C89ATOMIC_INLINE float c89atomic_fetch_xor_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15795 {
15796 c89atomic_if32 r;
15797 c89atomic_if32 x;
15798 x.f = src;
15799 r.i = c89atomic_fetch_xor_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15800 return r.f;
15801 }
15802 static C89ATOMIC_INLINE double c89atomic_fetch_xor_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15803 {
15804 c89atomic_if64 r;
15805 c89atomic_if64 x;
15806 x.f = src;
15807 r.i = c89atomic_fetch_xor_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15808 return r.f;
15809 }
15810 static C89ATOMIC_INLINE float c89atomic_fetch_and_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order)
15811 {
15812 c89atomic_if32 r;
15813 c89atomic_if32 x;
15814 x.f = src;
15815 r.i = c89atomic_fetch_and_explicit_32((volatile c89atomic_uint32*)dst, x.i, order);
15816 return r.f;
15817 }
15818 static C89ATOMIC_INLINE double c89atomic_fetch_and_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order)
15819 {
15820 c89atomic_if64 r;
15821 c89atomic_if64 x;
15822 x.f = src;
15823 r.i = c89atomic_fetch_and_explicit_64((volatile c89atomic_uint64*)dst, x.i, order);
15824 return r.f;
15825 }
15826 #define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
15827 #define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
15828 #define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15829 #define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15830 #define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst)
15831 #define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst)
15832 #define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15833 #define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15834 #define c89atomic_compare_exchange_strong_f32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15835 #define c89atomic_compare_exchange_strong_f64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15836 #define c89atomic_compare_exchange_weak_f32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15837 #define c89atomic_compare_exchange_weak_f64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst)
15838 #define c89atomic_fetch_add_f32(dst, src) c89atomic_fetch_add_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15839 #define c89atomic_fetch_add_f64(dst, src) c89atomic_fetch_add_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15840 #define c89atomic_fetch_sub_f32(dst, src) c89atomic_fetch_sub_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15841 #define c89atomic_fetch_sub_f64(dst, src) c89atomic_fetch_sub_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15842 #define c89atomic_fetch_or_f32(dst, src) c89atomic_fetch_or_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15843 #define c89atomic_fetch_or_f64(dst, src) c89atomic_fetch_or_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15844 #define c89atomic_fetch_xor_f32(dst, src) c89atomic_fetch_xor_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15845 #define c89atomic_fetch_xor_f64(dst, src) c89atomic_fetch_xor_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15846 #define c89atomic_fetch_and_f32(dst, src) c89atomic_fetch_and_explicit_f32(dst, src, c89atomic_memory_order_seq_cst)
15847 #define c89atomic_fetch_and_f64(dst, src) c89atomic_fetch_and_explicit_f64(dst, src, c89atomic_memory_order_seq_cst)
15848 static C89ATOMIC_INLINE float c89atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired)
15849 {
15850 c89atomic_if32 r;
15851 c89atomic_if32 e, d;
15852 e.f = expected;
15853 d.f = desired;
15854 r.i = c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, e.i, d.i);
15855 return r.f;
15856 }
15857 static C89ATOMIC_INLINE double c89atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired)
15858 {
15859 c89atomic_if64 r;
15860 c89atomic_if64 e, d;
15861 e.f = expected;
15862 d.f = desired;
15863 r.i = c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, e.i, d.i);
15864 return r.f;
15865 }
15866 typedef c89atomic_flag c89atomic_spinlock;
15867 static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock)
15868 {
15869 for (;;) {
15870 if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) {
15871 break;
15872 }
15873 while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
15874 }
15875 }
15876 }
15877 static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock)
15878 {
15879 c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release);
15880 }
15881 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
15882 #pragma GCC diagnostic pop
15883 #endif
15884 #if defined(__cplusplus)
15885 }
15886 #endif
15887 #endif
15888 /* c89atomic.h end */
15889
15890 #define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \
15891 static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \
15892 { \
15893 return (ma_##type)c89atomic_load_##c89TypeExtension(&x->value); \
15894 } \
15895 static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \
15896 { \
15897 c89atomic_store_##c89TypeExtension(&x->value, value); \
15898 } \
15899 static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \
15900 { \
15901 return (ma_##type)c89atomic_exchange_##c89TypeExtension(&x->value, value); \
15902 } \
15903 static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \
15904 { \
15905 return c89atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \
15906 } \
15907 static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \
15908 { \
15909 return (ma_##type)c89atomic_fetch_add_##c89TypeExtension(&x->value, y); \
15910 } \
15911 static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \
15912 { \
15913 return (ma_##type)c89atomic_fetch_sub_##c89TypeExtension(&x->value, y); \
15914 } \
15915 static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \
15916 { \
15917 return (ma_##type)c89atomic_fetch_or_##c89TypeExtension(&x->value, y); \
15918 } \
15919 static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \
15920 { \
15921 return (ma_##type)c89atomic_fetch_xor_##c89TypeExtension(&x->value, y); \
15922 } \
15923 static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \
15924 { \
15925 return (ma_##type)c89atomic_fetch_and_##c89TypeExtension(&x->value, y); \
15926 } \
15927 static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \
15928 { \
15929 return (ma_##type)c89atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \
15930 } \
15931
15932 #define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \
15933 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \
15934 { \
15935 return c89atomic_load_ptr((void**)&x->value); \
15936 } \
15937 static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \
15938 { \
15939 c89atomic_store_ptr((void**)&x->value, (void*)value); \
15940 } \
15941 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \
15942 { \
15943 return c89atomic_exchange_ptr((void**)&x->value, (void*)value); \
15944 } \
15945 static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \
15946 { \
15947 return c89atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \
15948 } \
15949 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \
15950 { \
15951 return (ma_##type*)c89atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \
15952 } \
15953
15954 MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32)
15955 MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32)
15956 MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64)
15957 MA_ATOMIC_SAFE_TYPE_IMPL(f32, float)
15958 MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32)
15959
15960 #if !defined(MA_NO_DEVICE_IO)
15961 MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state)
15962 #endif
15963
15964
15965 MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
15966 {
15967 /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */
15968 ma_uint64 outputFrameCount;
15969 ma_uint64 preliminaryInputFrameCountFromFrac;
15970 ma_uint64 preliminaryInputFrameCount;
15971
15972 if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) {
15973 return 0;
15974 }
15975
15976 if (sampleRateOut == sampleRateIn) {
15977 return frameCountIn;
15978 }
15979
15980 outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn;
15981
15982 preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut;
15983 preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac;
15984
15985 if (preliminaryInputFrameCount <= frameCountIn) {
15986 outputFrameCount += 1;
15987 }
15988
15989 return outputFrameCount;
15990 }
15991
15992 #ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
15993 #define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
15994 #endif
15995
15996
15997
15998 #if defined(MA_WIN32)
15999 static ma_result ma_result_from_GetLastError(DWORD error)
16000 {
16001 switch (error)
16002 {
16003 case ERROR_SUCCESS: return MA_SUCCESS;
16004 case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
16005 case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
16006 case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
16007 case ERROR_DISK_FULL: return MA_NO_SPACE;
16008 case ERROR_HANDLE_EOF: return MA_AT_END;
16009 case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
16010 case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
16011 case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
16012 case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
16013 case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
16014 default: break;
16015 }
16016
16017 return MA_ERROR;
16018 }
16019 #endif /* MA_WIN32 */
16020
16021
16022 /*******************************************************************************
16023
16024 Threading
16025
16026 *******************************************************************************/
16027 static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)
16028 {
16029 if (pSpinlock == NULL) {
16030 return MA_INVALID_ARGS;
16031 }
16032
16033 for (;;) {
16034 if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) {
16035 break;
16036 }
16037
16038 while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) {
16039 if (yield) {
16040 ma_yield();
16041 }
16042 }
16043 }
16044
16045 return MA_SUCCESS;
16046 }
16047
16048 MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)
16049 {
16050 return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
16051 }
16052
16053 MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock)
16054 {
16055 return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
16056 }
16057
16058 MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock)
16059 {
16060 if (pSpinlock == NULL) {
16061 return MA_INVALID_ARGS;
16062 }
16063
16064 c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release);
16065 return MA_SUCCESS;
16066 }
16067
16068
16069 #ifndef MA_NO_THREADING
16070 #if defined(MA_POSIX)
16071 #define MA_THREADCALL
16072 typedef void* ma_thread_result;
16073 #elif defined(MA_WIN32)
16074 #define MA_THREADCALL WINAPI
16075 typedef unsigned long ma_thread_result;
16076 #endif
16077
16078 typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
16079
16080 #ifdef MA_POSIX
16081 static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
16082 {
16083 int result;
16084 pthread_attr_t* pAttr = NULL;
16085
16086 #if !defined(__EMSCRIPTEN__)
16087 /* Try setting the thread priority. It's not critical if anything fails here. */
16088 pthread_attr_t attr;
16089 if (pthread_attr_init(&attr) == 0) {
16090 int scheduler = -1;
16091
16092 /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */
16093 pAttr = &attr;
16094
16095 /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */
16096 #if !defined(MA_BEOS)
16097 {
16098 if (priority == ma_thread_priority_idle) {
16099 #ifdef SCHED_IDLE
16100 if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
16101 scheduler = SCHED_IDLE;
16102 }
16103 #endif
16104 } else if (priority == ma_thread_priority_realtime) {
16105 #ifdef SCHED_FIFO
16106 if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {
16107 scheduler = SCHED_FIFO;
16108 }
16109 #endif
16110 #ifdef MA_LINUX
16111 } else {
16112 scheduler = sched_getscheduler(0);
16113 #endif
16114 }
16115 }
16116 #endif
16117
16118 if (stackSize > 0) {
16119 pthread_attr_setstacksize(&attr, stackSize);
16120 }
16121
16122 if (scheduler != -1) {
16123 int priorityMin = sched_get_priority_min(scheduler);
16124 int priorityMax = sched_get_priority_max(scheduler);
16125 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
16126
16127 struct sched_param sched;
16128 if (pthread_attr_getschedparam(&attr, &sched) == 0) {
16129 if (priority == ma_thread_priority_idle) {
16130 sched.sched_priority = priorityMin;
16131 } else if (priority == ma_thread_priority_realtime) {
16132 sched.sched_priority = priorityMax;
16133 } else {
16134 sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
16135 if (sched.sched_priority < priorityMin) {
16136 sched.sched_priority = priorityMin;
16137 }
16138 if (sched.sched_priority > priorityMax) {
16139 sched.sched_priority = priorityMax;
16140 }
16141 }
16142
16143 /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */
16144 pthread_attr_setschedparam(&attr, &sched);
16145 }
16146 }
16147 }
16148 #else
16149 /* It's the emscripten build. We'll have a few unused parameters. */
16150 (void)priority;
16151 (void)stackSize;
16152 #endif
16153
16154 result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData);
16155
16156 /* The thread attributes object is no longer required. */
16157 if (pAttr != NULL) {
16158 pthread_attr_destroy(pAttr);
16159 }
16160
16161 if (result != 0) {
16162 return ma_result_from_errno(result);
16163 }
16164
16165 return MA_SUCCESS;
16166 }
16167
16168 static void ma_thread_wait__posix(ma_thread* pThread)
16169 {
16170 pthread_join((pthread_t)*pThread, NULL);
16171 }
16172
16173
16174 static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
16175 {
16176 int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
16177 if (result != 0) {
16178 return ma_result_from_errno(result);
16179 }
16180
16181 return MA_SUCCESS;
16182 }
16183
16184 static void ma_mutex_uninit__posix(ma_mutex* pMutex)
16185 {
16186 pthread_mutex_destroy((pthread_mutex_t*)pMutex);
16187 }
16188
16189 static void ma_mutex_lock__posix(ma_mutex* pMutex)
16190 {
16191 pthread_mutex_lock((pthread_mutex_t*)pMutex);
16192 }
16193
16194 static void ma_mutex_unlock__posix(ma_mutex* pMutex)
16195 {
16196 pthread_mutex_unlock((pthread_mutex_t*)pMutex);
16197 }
16198
16199
16200 static ma_result ma_event_init__posix(ma_event* pEvent)
16201 {
16202 int result;
16203
16204 result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL);
16205 if (result != 0) {
16206 return ma_result_from_errno(result);
16207 }
16208
16209 result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL);
16210 if (result != 0) {
16211 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
16212 return ma_result_from_errno(result);
16213 }
16214
16215 pEvent->value = 0;
16216 return MA_SUCCESS;
16217 }
16218
16219 static void ma_event_uninit__posix(ma_event* pEvent)
16220 {
16221 pthread_cond_destroy((pthread_cond_t*)&pEvent->cond);
16222 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
16223 }
16224
16225 static ma_result ma_event_wait__posix(ma_event* pEvent)
16226 {
16227 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
16228 {
16229 while (pEvent->value == 0) {
16230 pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock);
16231 }
16232 pEvent->value = 0; /* Auto-reset. */
16233 }
16234 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
16235
16236 return MA_SUCCESS;
16237 }
16238
16239 static ma_result ma_event_signal__posix(ma_event* pEvent)
16240 {
16241 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
16242 {
16243 pEvent->value = 1;
16244 pthread_cond_signal((pthread_cond_t*)&pEvent->cond);
16245 }
16246 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
16247
16248 return MA_SUCCESS;
16249 }
16250
16251
16252 static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)
16253 {
16254 int result;
16255
16256 if (pSemaphore == NULL) {
16257 return MA_INVALID_ARGS;
16258 }
16259
16260 pSemaphore->value = initialValue;
16261
16262 result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL);
16263 if (result != 0) {
16264 return ma_result_from_errno(result); /* Failed to create mutex. */
16265 }
16266
16267 result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL);
16268 if (result != 0) {
16269 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
16270 return ma_result_from_errno(result); /* Failed to create condition variable. */
16271 }
16272
16273 return MA_SUCCESS;
16274 }
16275
16276 static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
16277 {
16278 if (pSemaphore == NULL) {
16279 return;
16280 }
16281
16282 pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond);
16283 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
16284 }
16285
16286 static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
16287 {
16288 if (pSemaphore == NULL) {
16289 return MA_INVALID_ARGS;
16290 }
16291
16292 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
16293 {
16294 /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */
16295 while (pSemaphore->value == 0) {
16296 pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock);
16297 }
16298
16299 pSemaphore->value -= 1;
16300 }
16301 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
16302
16303 return MA_SUCCESS;
16304 }
16305
16306 static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)
16307 {
16308 if (pSemaphore == NULL) {
16309 return MA_INVALID_ARGS;
16310 }
16311
16312 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
16313 {
16314 pSemaphore->value += 1;
16315 pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond);
16316 }
16317 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
16318
16319 return MA_SUCCESS;
16320 }
16321 #elif defined(MA_WIN32)
16322 static int ma_thread_priority_to_win32(ma_thread_priority priority)
16323 {
16324 switch (priority) {
16325 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
16326 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
16327 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
16328 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
16329 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
16330 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
16331 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
16332 default: return THREAD_PRIORITY_NORMAL;
16333 }
16334 }
16335
16336 static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
16337 {
16338 DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */
16339
16340 *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID);
16341 if (*pThread == NULL) {
16342 return ma_result_from_GetLastError(GetLastError());
16343 }
16344
16345 SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));
16346
16347 return MA_SUCCESS;
16348 }
16349
16350 static void ma_thread_wait__win32(ma_thread* pThread)
16351 {
16352 WaitForSingleObject((HANDLE)*pThread, INFINITE);
16353 CloseHandle((HANDLE)*pThread);
16354 }
16355
16356
16357 static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
16358 {
16359 *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
16360 if (*pMutex == NULL) {
16361 return ma_result_from_GetLastError(GetLastError());
16362 }
16363
16364 return MA_SUCCESS;
16365 }
16366
16367 static void ma_mutex_uninit__win32(ma_mutex* pMutex)
16368 {
16369 CloseHandle((HANDLE)*pMutex);
16370 }
16371
16372 static void ma_mutex_lock__win32(ma_mutex* pMutex)
16373 {
16374 WaitForSingleObject((HANDLE)*pMutex, INFINITE);
16375 }
16376
16377 static void ma_mutex_unlock__win32(ma_mutex* pMutex)
16378 {
16379 SetEvent((HANDLE)*pMutex);
16380 }
16381
16382
16383 static ma_result ma_event_init__win32(ma_event* pEvent)
16384 {
16385 *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
16386 if (*pEvent == NULL) {
16387 return ma_result_from_GetLastError(GetLastError());
16388 }
16389
16390 return MA_SUCCESS;
16391 }
16392
16393 static void ma_event_uninit__win32(ma_event* pEvent)
16394 {
16395 CloseHandle((HANDLE)*pEvent);
16396 }
16397
16398 static ma_result ma_event_wait__win32(ma_event* pEvent)
16399 {
16400 DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);
16401 if (result == WAIT_OBJECT_0) {
16402 return MA_SUCCESS;
16403 }
16404
16405 if (result == WAIT_TIMEOUT) {
16406 return MA_TIMEOUT;
16407 }
16408
16409 return ma_result_from_GetLastError(GetLastError());
16410 }
16411
16412 static ma_result ma_event_signal__win32(ma_event* pEvent)
16413 {
16414 BOOL result = SetEvent((HANDLE)*pEvent);
16415 if (result == 0) {
16416 return ma_result_from_GetLastError(GetLastError());
16417 }
16418
16419 return MA_SUCCESS;
16420 }
16421
16422
16423 static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)
16424 {
16425 *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
16426 if (*pSemaphore == NULL) {
16427 return ma_result_from_GetLastError(GetLastError());
16428 }
16429
16430 return MA_SUCCESS;
16431 }
16432
16433 static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
16434 {
16435 CloseHandle((HANDLE)*pSemaphore);
16436 }
16437
16438 static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
16439 {
16440 DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);
16441 if (result == WAIT_OBJECT_0) {
16442 return MA_SUCCESS;
16443 }
16444
16445 if (result == WAIT_TIMEOUT) {
16446 return MA_TIMEOUT;
16447 }
16448
16449 return ma_result_from_GetLastError(GetLastError());
16450 }
16451
16452 static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)
16453 {
16454 BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);
16455 if (result == 0) {
16456 return ma_result_from_GetLastError(GetLastError());
16457 }
16458
16459 return MA_SUCCESS;
16460 }
16461 #endif
16462
16463 typedef struct
16464 {
16465 ma_thread_entry_proc entryProc;
16466 void* pData;
16467 ma_allocation_callbacks allocationCallbacks;
16468 } ma_thread_proxy_data;
16469
16470 static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData)
16471 {
16472 ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData;
16473 ma_thread_entry_proc entryProc;
16474 void* pEntryProcData;
16475 ma_thread_result result;
16476
16477 #if defined(MA_ON_THREAD_ENTRY)
16478 MA_ON_THREAD_ENTRY
16479 #endif
16480
16481 entryProc = pProxyData->entryProc;
16482 pEntryProcData = pProxyData->pData;
16483
16484 /* Free the proxy data before getting into the real thread entry proc. */
16485 ma_free(pProxyData, &pProxyData->allocationCallbacks);
16486
16487 result = entryProc(pEntryProcData);
16488
16489 #if defined(MA_ON_THREAD_EXIT)
16490 MA_ON_THREAD_EXIT
16491 #endif
16492
16493 return result;
16494 }
16495
16496 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)
16497 {
16498 ma_result result;
16499 ma_thread_proxy_data* pProxyData;
16500
16501 if (pThread == NULL || entryProc == NULL) {
16502 return MA_INVALID_ARGS;
16503 }
16504
16505 pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */
16506 if (pProxyData == NULL) {
16507 return MA_OUT_OF_MEMORY;
16508 }
16509
16510 #if defined(MA_THREAD_DEFAULT_STACK_SIZE)
16511 if (stackSize == 0) {
16512 stackSize = MA_THREAD_DEFAULT_STACK_SIZE;
16513 }
16514 #endif
16515
16516 pProxyData->entryProc = entryProc;
16517 pProxyData->pData = pData;
16518 ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks);
16519
16520 #if defined(MA_POSIX)
16521 result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
16522 #elif defined(MA_WIN32)
16523 result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
16524 #endif
16525
16526 if (result != MA_SUCCESS) {
16527 ma_free(pProxyData, pAllocationCallbacks);
16528 return result;
16529 }
16530
16531 return MA_SUCCESS;
16532 }
16533
16534 static void ma_thread_wait(ma_thread* pThread)
16535 {
16536 if (pThread == NULL) {
16537 return;
16538 }
16539
16540 #if defined(MA_POSIX)
16541 ma_thread_wait__posix(pThread);
16542 #elif defined(MA_WIN32)
16543 ma_thread_wait__win32(pThread);
16544 #endif
16545 }
16546
16547
16548 MA_API ma_result ma_mutex_init(ma_mutex* pMutex)
16549 {
16550 if (pMutex == NULL) {
16551 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16552 return MA_INVALID_ARGS;
16553 }
16554
16555 #if defined(MA_POSIX)
16556 return ma_mutex_init__posix(pMutex);
16557 #elif defined(MA_WIN32)
16558 return ma_mutex_init__win32(pMutex);
16559 #endif
16560 }
16561
16562 MA_API void ma_mutex_uninit(ma_mutex* pMutex)
16563 {
16564 if (pMutex == NULL) {
16565 return;
16566 }
16567
16568 #if defined(MA_POSIX)
16569 ma_mutex_uninit__posix(pMutex);
16570 #elif defined(MA_WIN32)
16571 ma_mutex_uninit__win32(pMutex);
16572 #endif
16573 }
16574
16575 MA_API void ma_mutex_lock(ma_mutex* pMutex)
16576 {
16577 if (pMutex == NULL) {
16578 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16579 return;
16580 }
16581
16582 #if defined(MA_POSIX)
16583 ma_mutex_lock__posix(pMutex);
16584 #elif defined(MA_WIN32)
16585 ma_mutex_lock__win32(pMutex);
16586 #endif
16587 }
16588
16589 MA_API void ma_mutex_unlock(ma_mutex* pMutex)
16590 {
16591 if (pMutex == NULL) {
16592 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16593 return;
16594 }
16595
16596 #if defined(MA_POSIX)
16597 ma_mutex_unlock__posix(pMutex);
16598 #elif defined(MA_WIN32)
16599 ma_mutex_unlock__win32(pMutex);
16600 #endif
16601 }
16602
16603
16604 MA_API ma_result ma_event_init(ma_event* pEvent)
16605 {
16606 if (pEvent == NULL) {
16607 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16608 return MA_INVALID_ARGS;
16609 }
16610
16611 #if defined(MA_POSIX)
16612 return ma_event_init__posix(pEvent);
16613 #elif defined(MA_WIN32)
16614 return ma_event_init__win32(pEvent);
16615 #endif
16616 }
16617
16618 #if 0
16619 static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks)
16620 {
16621 ma_result result;
16622 ma_event* pEvent;
16623
16624 if (ppEvent == NULL) {
16625 return MA_INVALID_ARGS;
16626 }
16627
16628 *ppEvent = NULL;
16629
16630 pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks);
16631 if (pEvent == NULL) {
16632 return MA_OUT_OF_MEMORY;
16633 }
16634
16635 result = ma_event_init(pEvent);
16636 if (result != MA_SUCCESS) {
16637 ma_free(pEvent, pAllocationCallbacks);
16638 return result;
16639 }
16640
16641 *ppEvent = pEvent;
16642 return result;
16643 }
16644 #endif
16645
16646 MA_API void ma_event_uninit(ma_event* pEvent)
16647 {
16648 if (pEvent == NULL) {
16649 return;
16650 }
16651
16652 #if defined(MA_POSIX)
16653 ma_event_uninit__posix(pEvent);
16654 #elif defined(MA_WIN32)
16655 ma_event_uninit__win32(pEvent);
16656 #endif
16657 }
16658
16659 #if 0
16660 static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks)
16661 {
16662 if (pEvent == NULL) {
16663 return;
16664 }
16665
16666 ma_event_uninit(pEvent);
16667 ma_free(pEvent, pAllocationCallbacks);
16668 }
16669 #endif
16670
16671 MA_API ma_result ma_event_wait(ma_event* pEvent)
16672 {
16673 if (pEvent == NULL) {
16674 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
16675 return MA_INVALID_ARGS;
16676 }
16677
16678 #if defined(MA_POSIX)
16679 return ma_event_wait__posix(pEvent);
16680 #elif defined(MA_WIN32)
16681 return ma_event_wait__win32(pEvent);
16682 #endif
16683 }
16684
16685 MA_API ma_result ma_event_signal(ma_event* pEvent)
16686 {
16687 if (pEvent == NULL) {
16688 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
16689 return MA_INVALID_ARGS;
16690 }
16691
16692 #if defined(MA_POSIX)
16693 return ma_event_signal__posix(pEvent);
16694 #elif defined(MA_WIN32)
16695 return ma_event_signal__win32(pEvent);
16696 #endif
16697 }
16698
16699
16700 MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
16701 {
16702 if (pSemaphore == NULL) {
16703 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16704 return MA_INVALID_ARGS;
16705 }
16706
16707 #if defined(MA_POSIX)
16708 return ma_semaphore_init__posix(initialValue, pSemaphore);
16709 #elif defined(MA_WIN32)
16710 return ma_semaphore_init__win32(initialValue, pSemaphore);
16711 #endif
16712 }
16713
16714 MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
16715 {
16716 if (pSemaphore == NULL) {
16717 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16718 return;
16719 }
16720
16721 #if defined(MA_POSIX)
16722 ma_semaphore_uninit__posix(pSemaphore);
16723 #elif defined(MA_WIN32)
16724 ma_semaphore_uninit__win32(pSemaphore);
16725 #endif
16726 }
16727
16728 MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)
16729 {
16730 if (pSemaphore == NULL) {
16731 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16732 return MA_INVALID_ARGS;
16733 }
16734
16735 #if defined(MA_POSIX)
16736 return ma_semaphore_wait__posix(pSemaphore);
16737 #elif defined(MA_WIN32)
16738 return ma_semaphore_wait__win32(pSemaphore);
16739 #endif
16740 }
16741
16742 MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)
16743 {
16744 if (pSemaphore == NULL) {
16745 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
16746 return MA_INVALID_ARGS;
16747 }
16748
16749 #if defined(MA_POSIX)
16750 return ma_semaphore_release__posix(pSemaphore);
16751 #elif defined(MA_WIN32)
16752 return ma_semaphore_release__win32(pSemaphore);
16753 #endif
16754 }
16755 #else
16756 /* 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. */
16757 #ifndef MA_NO_DEVICE_IO
16758 #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
16759 #endif
16760 #endif /* MA_NO_THREADING */
16761
16762
16763
16764 #define MA_FENCE_COUNTER_MAX 0x7FFFFFFF
16765
16766 MA_API ma_result ma_fence_init(ma_fence* pFence)
16767 {
16768 if (pFence == NULL) {
16769 return MA_INVALID_ARGS;
16770 }
16771
16772 MA_ZERO_OBJECT(pFence);
16773 pFence->counter = 0;
16774
16775 #ifndef MA_NO_THREADING
16776 {
16777 ma_result result;
16778
16779 result = ma_event_init(&pFence->e);
16780 if (result != MA_SUCCESS) {
16781 return result;
16782 }
16783 }
16784 #endif
16785
16786 return MA_SUCCESS;
16787 }
16788
16789 MA_API void ma_fence_uninit(ma_fence* pFence)
16790 {
16791 if (pFence == NULL) {
16792 return;
16793 }
16794
16795 #ifndef MA_NO_THREADING
16796 {
16797 ma_event_uninit(&pFence->e);
16798 }
16799 #endif
16800
16801 MA_ZERO_OBJECT(pFence);
16802 }
16803
16804 MA_API ma_result ma_fence_acquire(ma_fence* pFence)
16805 {
16806 if (pFence == NULL) {
16807 return MA_INVALID_ARGS;
16808 }
16809
16810 for (;;) {
16811 ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
16812 ma_uint32 newCounter = oldCounter + 1;
16813
16814 /* Make sure we're not about to exceed our maximum value. */
16815 if (newCounter > MA_FENCE_COUNTER_MAX) {
16816 MA_ASSERT(MA_FALSE);
16817 return MA_OUT_OF_RANGE;
16818 }
16819
16820 if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
16821 return MA_SUCCESS;
16822 } else {
16823 if (oldCounter == MA_FENCE_COUNTER_MAX) {
16824 MA_ASSERT(MA_FALSE);
16825 return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */
16826 }
16827 }
16828 }
16829
16830 /* Should never get here. */
16831 /*return MA_SUCCESS;*/
16832 }
16833
16834 MA_API ma_result ma_fence_release(ma_fence* pFence)
16835 {
16836 if (pFence == NULL) {
16837 return MA_INVALID_ARGS;
16838 }
16839
16840 for (;;) {
16841 ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter);
16842 ma_uint32 newCounter = oldCounter - 1;
16843
16844 if (oldCounter == 0) {
16845 MA_ASSERT(MA_FALSE);
16846 return MA_INVALID_OPERATION; /* Acquire/release mismatch. */
16847 }
16848
16849 if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
16850 #ifndef MA_NO_THREADING
16851 {
16852 if (newCounter == 0) {
16853 ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */
16854 }
16855 }
16856 #endif
16857
16858 return MA_SUCCESS;
16859 } else {
16860 if (oldCounter == 0) {
16861 MA_ASSERT(MA_FALSE);
16862 return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */
16863 }
16864 }
16865 }
16866
16867 /* Should never get here. */
16868 /*return MA_SUCCESS;*/
16869 }
16870
16871 MA_API ma_result ma_fence_wait(ma_fence* pFence)
16872 {
16873 if (pFence == NULL) {
16874 return MA_INVALID_ARGS;
16875 }
16876
16877 for (;;) {
16878 ma_uint32 counter;
16879
16880 counter = c89atomic_load_32(&pFence->counter);
16881 if (counter == 0) {
16882 /*
16883 Counter has hit zero. By the time we get here some other thread may have acquired the
16884 fence again, but that is where the caller needs to take care with how they se the fence.
16885 */
16886 return MA_SUCCESS;
16887 }
16888
16889 /* Getting here means the counter is > 0. We'll need to wait for something to happen. */
16890 #ifndef MA_NO_THREADING
16891 {
16892 ma_result result;
16893
16894 result = ma_event_wait(&pFence->e);
16895 if (result != MA_SUCCESS) {
16896 return result;
16897 }
16898 }
16899 #endif
16900 }
16901
16902 /* Should never get here. */
16903 /*return MA_INVALID_OPERATION;*/
16904 }
16905
16906
16907 MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification)
16908 {
16909 ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;
16910
16911 if (pNotification == NULL) {
16912 return MA_INVALID_ARGS;
16913 }
16914
16915 if (pNotificationCallbacks->onSignal == NULL) {
16916 return MA_NOT_IMPLEMENTED;
16917 }
16918
16919 pNotificationCallbacks->onSignal(pNotification);
16920 return MA_INVALID_ARGS;
16921 }
16922
16923
16924 static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification)
16925 {
16926 ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE;
16927 }
16928
16929 MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll)
16930 {
16931 if (pNotificationPoll == NULL) {
16932 return MA_INVALID_ARGS;
16933 }
16934
16935 pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal;
16936 pNotificationPoll->signalled = MA_FALSE;
16937
16938 return MA_SUCCESS;
16939 }
16940
16941 MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll)
16942 {
16943 if (pNotificationPoll == NULL) {
16944 return MA_FALSE;
16945 }
16946
16947 return pNotificationPoll->signalled;
16948 }
16949
16950
16951 static void ma_async_notification_event__on_signal(ma_async_notification* pNotification)
16952 {
16953 ma_async_notification_event_signal((ma_async_notification_event*)pNotification);
16954 }
16955
16956 MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent)
16957 {
16958 if (pNotificationEvent == NULL) {
16959 return MA_INVALID_ARGS;
16960 }
16961
16962 pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal;
16963
16964 #ifndef MA_NO_THREADING
16965 {
16966 ma_result result;
16967
16968 result = ma_event_init(&pNotificationEvent->e);
16969 if (result != MA_SUCCESS) {
16970 return result;
16971 }
16972
16973 return MA_SUCCESS;
16974 }
16975 #else
16976 {
16977 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
16978 }
16979 #endif
16980 }
16981
16982 MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent)
16983 {
16984 if (pNotificationEvent == NULL) {
16985 return MA_INVALID_ARGS;
16986 }
16987
16988 #ifndef MA_NO_THREADING
16989 {
16990 ma_event_uninit(&pNotificationEvent->e);
16991 return MA_SUCCESS;
16992 }
16993 #else
16994 {
16995 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
16996 }
16997 #endif
16998 }
16999
17000 MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent)
17001 {
17002 if (pNotificationEvent == NULL) {
17003 return MA_INVALID_ARGS;
17004 }
17005
17006 #ifndef MA_NO_THREADING
17007 {
17008 return ma_event_wait(&pNotificationEvent->e);
17009 }
17010 #else
17011 {
17012 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
17013 }
17014 #endif
17015 }
17016
17017 MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent)
17018 {
17019 if (pNotificationEvent == NULL) {
17020 return MA_INVALID_ARGS;
17021 }
17022
17023 #ifndef MA_NO_THREADING
17024 {
17025 return ma_event_signal(&pNotificationEvent->e);
17026 }
17027 #else
17028 {
17029 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
17030 }
17031 #endif
17032 }
17033
17034
17035
17036 /************************************************************************************************************************************************************
17037
17038 Job Queue
17039
17040 ************************************************************************************************************************************************************/
17041 MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity)
17042 {
17043 ma_slot_allocator_config config;
17044
17045 MA_ZERO_OBJECT(&config);
17046 config.capacity = capacity;
17047
17048 return config;
17049 }
17050
17051
17052 static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity)
17053 {
17054 ma_uint32 cap = slotCapacity / 32;
17055 if ((slotCapacity % 32) != 0) {
17056 cap += 1;
17057 }
17058
17059 return cap;
17060 }
17061
17062 static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator)
17063 {
17064 return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity);
17065 }
17066
17067
17068 typedef struct
17069 {
17070 size_t sizeInBytes;
17071 size_t groupsOffset;
17072 size_t slotsOffset;
17073 } ma_slot_allocator_heap_layout;
17074
17075 static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout)
17076 {
17077 MA_ASSERT(pHeapLayout != NULL);
17078
17079 MA_ZERO_OBJECT(pHeapLayout);
17080
17081 if (pConfig == NULL) {
17082 return MA_INVALID_ARGS;
17083 }
17084
17085 if (pConfig->capacity == 0) {
17086 return MA_INVALID_ARGS;
17087 }
17088
17089 pHeapLayout->sizeInBytes = 0;
17090
17091 /* Groups. */
17092 pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes;
17093 pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group));
17094
17095 /* Slots. */
17096 pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes;
17097 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32));
17098
17099 return MA_SUCCESS;
17100 }
17101
17102 MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes)
17103 {
17104 ma_result result;
17105 ma_slot_allocator_heap_layout layout;
17106
17107 if (pHeapSizeInBytes == NULL) {
17108 return MA_INVALID_ARGS;
17109 }
17110
17111 *pHeapSizeInBytes = 0;
17112
17113 result = ma_slot_allocator_get_heap_layout(pConfig, &layout);
17114 if (result != MA_SUCCESS) {
17115 return result;
17116 }
17117
17118 *pHeapSizeInBytes = layout.sizeInBytes;
17119
17120 return result;
17121 }
17122
17123 MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator)
17124 {
17125 ma_result result;
17126 ma_slot_allocator_heap_layout heapLayout;
17127
17128 if (pAllocator == NULL) {
17129 return MA_INVALID_ARGS;
17130 }
17131
17132 MA_ZERO_OBJECT(pAllocator);
17133
17134 if (pHeap == NULL) {
17135 return MA_INVALID_ARGS;
17136 }
17137
17138 result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout);
17139 if (result != MA_SUCCESS) {
17140 return result;
17141 }
17142
17143 pAllocator->_pHeap = pHeap;
17144 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
17145
17146 pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset);
17147 pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset);
17148 pAllocator->capacity = pConfig->capacity;
17149
17150 return MA_SUCCESS;
17151 }
17152
17153 MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator)
17154 {
17155 ma_result result;
17156 size_t heapSizeInBytes;
17157 void* pHeap;
17158
17159 result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes);
17160 if (result != MA_SUCCESS) {
17161 return result; /* Failed to retrieve the size of the heap allocation. */
17162 }
17163
17164 if (heapSizeInBytes > 0) {
17165 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
17166 if (pHeap == NULL) {
17167 return MA_OUT_OF_MEMORY;
17168 }
17169 } else {
17170 pHeap = NULL;
17171 }
17172
17173 result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator);
17174 if (result != MA_SUCCESS) {
17175 ma_free(pHeap, pAllocationCallbacks);
17176 return result;
17177 }
17178
17179 pAllocator->_ownsHeap = MA_TRUE;
17180 return MA_SUCCESS;
17181 }
17182
17183 MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks)
17184 {
17185 if (pAllocator == NULL) {
17186 return;
17187 }
17188
17189 if (pAllocator->_ownsHeap) {
17190 ma_free(pAllocator->_pHeap, pAllocationCallbacks);
17191 }
17192 }
17193
17194 MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot)
17195 {
17196 ma_uint32 iAttempt;
17197 const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */
17198
17199 if (pAllocator == NULL || pSlot == NULL) {
17200 return MA_INVALID_ARGS;
17201 }
17202
17203 for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) {
17204 /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */
17205 ma_uint32 iGroup;
17206 for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) {
17207 /* CAS */
17208 for (;;) {
17209 ma_uint32 oldBitfield;
17210 ma_uint32 newBitfield;
17211 ma_uint32 bitOffset;
17212
17213 oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
17214
17215 /* Fast check to see if anything is available. */
17216 if (oldBitfield == 0xFFFFFFFF) {
17217 break; /* No available bits in this bitfield. */
17218 }
17219
17220 bitOffset = ma_ffs_32(~oldBitfield);
17221 MA_ASSERT(bitOffset < 32);
17222
17223 newBitfield = oldBitfield | (1 << bitOffset);
17224
17225 if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
17226 ma_uint32 slotIndex;
17227
17228 /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */
17229 c89atomic_fetch_add_32(&pAllocator->count, 1);
17230
17231 /* The slot index is required for constructing the output value. */
17232 slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */
17233 if (slotIndex >= pAllocator->capacity) {
17234 return MA_OUT_OF_MEMORY;
17235 }
17236
17237 /* Increment the reference count before constructing the output value. */
17238 pAllocator->pSlots[slotIndex] += 1;
17239
17240 /* Construct the output value. */
17241 *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex);
17242
17243 return MA_SUCCESS;
17244 }
17245 }
17246 }
17247
17248 /* 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. */
17249 if (pAllocator->count < pAllocator->capacity) {
17250 ma_yield();
17251 } else {
17252 return MA_OUT_OF_MEMORY;
17253 }
17254 }
17255
17256 /* We couldn't find a slot within the maximum number of attempts. */
17257 return MA_OUT_OF_MEMORY;
17258 }
17259
17260 MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot)
17261 {
17262 ma_uint32 iGroup;
17263 ma_uint32 iBit;
17264
17265 if (pAllocator == NULL) {
17266 return MA_INVALID_ARGS;
17267 }
17268
17269 iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */
17270 iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */
17271
17272 if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) {
17273 return MA_INVALID_ARGS;
17274 }
17275
17276 MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */
17277
17278 while (c89atomic_load_32(&pAllocator->count) > 0) {
17279 /* CAS */
17280 ma_uint32 oldBitfield;
17281 ma_uint32 newBitfield;
17282
17283 oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
17284 newBitfield = oldBitfield & ~(1 << iBit);
17285
17286 /* Debugging for checking for double-frees. */
17287 #if defined(MA_DEBUG_OUTPUT)
17288 {
17289 if ((oldBitfield & (1 << iBit)) == 0) {
17290 MA_ASSERT(MA_FALSE); /* Double free detected.*/
17291 }
17292 }
17293 #endif
17294
17295 if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
17296 c89atomic_fetch_sub_32(&pAllocator->count, 1);
17297 return MA_SUCCESS;
17298 }
17299 }
17300
17301 /* Getting here means there are no allocations available for freeing. */
17302 return MA_INVALID_OPERATION;
17303 }
17304
17305
17306 #define MA_JOB_ID_NONE ~((ma_uint64)0)
17307 #define MA_JOB_SLOT_NONE (ma_uint16)(~0)
17308
17309 static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc)
17310 {
17311 return (ma_uint32)(toc >> 32);
17312 }
17313
17314 static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc)
17315 {
17316 return (ma_uint16)(toc & 0x0000FFFF);
17317 }
17318
17319 static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc)
17320 {
17321 return (ma_uint16)((toc & 0xFFFF0000) >> 16);
17322 }
17323
17324 static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc)
17325 {
17326 return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc);
17327 }
17328
17329 static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount)
17330 {
17331 /* Clear the reference count first. */
17332 toc = toc & ~((ma_uint64)0xFFFFFFFF << 32);
17333 toc = toc | ((ma_uint64)refcount << 32);
17334
17335 return toc;
17336 }
17337
17338
17339 MA_API ma_job ma_job_init(ma_uint16 code)
17340 {
17341 ma_job job;
17342
17343 MA_ZERO_OBJECT(&job);
17344 job.toc.breakup.code = code;
17345 job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */
17346 job.next = MA_JOB_ID_NONE;
17347
17348 return job;
17349 }
17350
17351
17352 static ma_result ma_job_process__noop(ma_job* pJob);
17353 static ma_result ma_job_process__quit(ma_job* pJob);
17354 static ma_result ma_job_process__custom(ma_job* pJob);
17355 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob);
17356 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob);
17357 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob);
17358 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob);
17359 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob);
17360 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob);
17361 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob);
17362 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob);
17363 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob);
17364
17365 #if !defined(MA_NO_DEVICE_IO)
17366 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob);
17367 #endif
17368
17369 static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] =
17370 {
17371 /* Miscellaneous. */
17372 ma_job_process__quit, /* MA_JOB_TYPE_QUIT */
17373 ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */
17374
17375 /* Resource Manager. */
17376 ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */
17377 ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */
17378 ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */
17379 ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */
17380 ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */
17381 ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */
17382 ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */
17383 ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */
17384 ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */
17385
17386 /* Device. */
17387 #if !defined(MA_NO_DEVICE_IO)
17388 ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/
17389 #endif
17390 };
17391
17392 MA_API ma_result ma_job_process(ma_job* pJob)
17393 {
17394 if (pJob == NULL) {
17395 return MA_INVALID_ARGS;
17396 }
17397
17398 if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) {
17399 return MA_INVALID_OPERATION;
17400 }
17401
17402 return g_jobVTable[pJob->toc.breakup.code](pJob);
17403 }
17404
17405 static ma_result ma_job_process__noop(ma_job* pJob)
17406 {
17407 MA_ASSERT(pJob != NULL);
17408
17409 /* No-op. */
17410 (void)pJob;
17411
17412 return MA_SUCCESS;
17413 }
17414
17415 static ma_result ma_job_process__quit(ma_job* pJob)
17416 {
17417 return ma_job_process__noop(pJob);
17418 }
17419
17420 static ma_result ma_job_process__custom(ma_job* pJob)
17421 {
17422 MA_ASSERT(pJob != NULL);
17423
17424 /* No-op if there's no callback. */
17425 if (pJob->data.custom.proc == NULL) {
17426 return MA_SUCCESS;
17427 }
17428
17429 return pJob->data.custom.proc(pJob);
17430 }
17431
17432
17433
17434 MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity)
17435 {
17436 ma_job_queue_config config;
17437
17438 config.flags = flags;
17439 config.capacity = capacity;
17440
17441 return config;
17442 }
17443
17444
17445 typedef struct
17446 {
17447 size_t sizeInBytes;
17448 size_t allocatorOffset;
17449 size_t jobsOffset;
17450 } ma_job_queue_heap_layout;
17451
17452 static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout)
17453 {
17454 ma_result result;
17455
17456 MA_ASSERT(pHeapLayout != NULL);
17457
17458 MA_ZERO_OBJECT(pHeapLayout);
17459
17460 if (pConfig == NULL) {
17461 return MA_INVALID_ARGS;
17462 }
17463
17464 if (pConfig->capacity == 0) {
17465 return MA_INVALID_ARGS;
17466 }
17467
17468 pHeapLayout->sizeInBytes = 0;
17469
17470 /* Allocator. */
17471 {
17472 ma_slot_allocator_config allocatorConfig;
17473 size_t allocatorHeapSizeInBytes;
17474
17475 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
17476 result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes);
17477 if (result != MA_SUCCESS) {
17478 return result;
17479 }
17480
17481 pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes;
17482 pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes;
17483 }
17484
17485 /* Jobs. */
17486 pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes;
17487 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job));
17488
17489 return MA_SUCCESS;
17490 }
17491
17492 MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes)
17493 {
17494 ma_result result;
17495 ma_job_queue_heap_layout layout;
17496
17497 if (pHeapSizeInBytes == NULL) {
17498 return MA_INVALID_ARGS;
17499 }
17500
17501 *pHeapSizeInBytes = 0;
17502
17503 result = ma_job_queue_get_heap_layout(pConfig, &layout);
17504 if (result != MA_SUCCESS) {
17505 return result;
17506 }
17507
17508 *pHeapSizeInBytes = layout.sizeInBytes;
17509
17510 return MA_SUCCESS;
17511 }
17512
17513 MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue)
17514 {
17515 ma_result result;
17516 ma_job_queue_heap_layout heapLayout;
17517 ma_slot_allocator_config allocatorConfig;
17518
17519 if (pQueue == NULL) {
17520 return MA_INVALID_ARGS;
17521 }
17522
17523 MA_ZERO_OBJECT(pQueue);
17524
17525 result = ma_job_queue_get_heap_layout(pConfig, &heapLayout);
17526 if (result != MA_SUCCESS) {
17527 return result;
17528 }
17529
17530 pQueue->_pHeap = pHeap;
17531 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
17532
17533 pQueue->flags = pConfig->flags;
17534 pQueue->capacity = pConfig->capacity;
17535 pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset);
17536
17537 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
17538 result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator);
17539 if (result != MA_SUCCESS) {
17540 return result;
17541 }
17542
17543 /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */
17544 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
17545 #ifndef MA_NO_THREADING
17546 {
17547 ma_semaphore_init(0, &pQueue->sem);
17548 }
17549 #else
17550 {
17551 /* Threading is disabled and we've requested non-blocking mode. */
17552 return MA_INVALID_OPERATION;
17553 }
17554 #endif
17555 }
17556
17557 /*
17558 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
17559 just a dummy item for giving us the first item in the list which is stored in the "next" member.
17560 */
17561 ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */
17562 pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;
17563 pQueue->tail = pQueue->head;
17564
17565 return MA_SUCCESS;
17566 }
17567
17568 MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue)
17569 {
17570 ma_result result;
17571 size_t heapSizeInBytes;
17572 void* pHeap;
17573
17574 result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes);
17575 if (result != MA_SUCCESS) {
17576 return result;
17577 }
17578
17579 if (heapSizeInBytes > 0) {
17580 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
17581 if (pHeap == NULL) {
17582 return MA_OUT_OF_MEMORY;
17583 }
17584 } else {
17585 pHeap = NULL;
17586 }
17587
17588 result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue);
17589 if (result != MA_SUCCESS) {
17590 ma_free(pHeap, pAllocationCallbacks);
17591 return result;
17592 }
17593
17594 pQueue->_ownsHeap = MA_TRUE;
17595 return MA_SUCCESS;
17596 }
17597
17598 MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks)
17599 {
17600 if (pQueue == NULL) {
17601 return;
17602 }
17603
17604 /* All we need to do is uninitialize the semaphore. */
17605 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
17606 #ifndef MA_NO_THREADING
17607 {
17608 ma_semaphore_uninit(&pQueue->sem);
17609 }
17610 #else
17611 {
17612 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
17613 }
17614 #endif
17615 }
17616
17617 ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks);
17618
17619 if (pQueue->_ownsHeap) {
17620 ma_free(pQueue->_pHeap, pAllocationCallbacks);
17621 }
17622 }
17623
17624 static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)
17625 {
17626 /* The new counter is taken from the expected value. */
17627 return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected;
17628 }
17629
17630 MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob)
17631 {
17632 /*
17633 Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors
17634 */
17635 ma_result result;
17636 ma_uint64 slot;
17637 ma_uint64 tail;
17638 ma_uint64 next;
17639
17640 if (pQueue == NULL || pJob == NULL) {
17641 return MA_INVALID_ARGS;
17642 }
17643
17644 /* We need a new slot. */
17645 result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);
17646 if (result != MA_SUCCESS) {
17647 return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */
17648 }
17649
17650 /* At this point we should have a slot to place the job. */
17651 MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity);
17652
17653 /* We need to put the job into memory before we do anything. */
17654 pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob;
17655 pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */
17656 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. */
17657 pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */
17658
17659 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
17660 ma_spinlock_lock(&pQueue->lock);
17661 #endif
17662 {
17663 /* 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. */
17664 for (;;) {
17665 tail = c89atomic_load_64(&pQueue->tail);
17666 next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next);
17667
17668 if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) {
17669 if (ma_job_extract_slot(next) == 0xFFFF) {
17670 if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {
17671 break;
17672 }
17673 } else {
17674 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
17675 }
17676 }
17677 }
17678 ma_job_queue_cas(&pQueue->tail, tail, slot);
17679 }
17680 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
17681 ma_spinlock_unlock(&pQueue->lock);
17682 #endif
17683
17684
17685 /* Signal the semaphore as the last step if we're using synchronous mode. */
17686 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
17687 #ifndef MA_NO_THREADING
17688 {
17689 ma_semaphore_release(&pQueue->sem);
17690 }
17691 #else
17692 {
17693 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
17694 }
17695 #endif
17696 }
17697
17698 return MA_SUCCESS;
17699 }
17700
17701 MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob)
17702 {
17703 ma_uint64 head;
17704 ma_uint64 tail;
17705 ma_uint64 next;
17706
17707 if (pQueue == NULL || pJob == NULL) {
17708 return MA_INVALID_ARGS;
17709 }
17710
17711 /* If we're running in synchronous mode we'll need to wait on a semaphore. */
17712 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
17713 #ifndef MA_NO_THREADING
17714 {
17715 ma_semaphore_wait(&pQueue->sem);
17716 }
17717 #else
17718 {
17719 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
17720 }
17721 #endif
17722 }
17723
17724 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
17725 ma_spinlock_lock(&pQueue->lock);
17726 #endif
17727 {
17728 /*
17729 BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below
17730 is stored. One thread can fall through to the freeing of this item while another is still using "head" for the
17731 retrieval of the "next" variable.
17732
17733 The slot allocator might need to make use of some reference counting to ensure it's only truely freed when
17734 there are no more references to the item. This must be fixed before removing these locks.
17735 */
17736
17737 /* Now we need to remove the root item from the list. */
17738 for (;;) {
17739 head = c89atomic_load_64(&pQueue->head);
17740 tail = c89atomic_load_64(&pQueue->tail);
17741 next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next);
17742
17743 if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) {
17744 if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) {
17745 if (ma_job_extract_slot(next) == 0xFFFF) {
17746 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
17747 ma_spinlock_unlock(&pQueue->lock);
17748 #endif
17749 return MA_NO_DATA_AVAILABLE;
17750 }
17751 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
17752 } else {
17753 *pJob = pQueue->pJobs[ma_job_extract_slot(next)];
17754 if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) {
17755 break;
17756 }
17757 }
17758 }
17759 }
17760 }
17761 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
17762 ma_spinlock_unlock(&pQueue->lock);
17763 #endif
17764
17765 ma_slot_allocator_free(&pQueue->allocator, head);
17766
17767 /*
17768 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
17769 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
17770 possible.
17771 */
17772 if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) {
17773 ma_job_queue_post(pQueue, pJob);
17774 return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */
17775 }
17776
17777 return MA_SUCCESS;
17778 }
17779
17780
17781
17782
17783 /************************************************************************************************************************************************************
17784 *************************************************************************************************************************************************************
17785
17786 DEVICE I/O
17787 ==========
17788
17789 *************************************************************************************************************************************************************
17790 ************************************************************************************************************************************************************/
17791
17792 /* Disable run-time linking on certain backends and platforms. */
17793 #ifndef MA_NO_RUNTIME_LINKING
17794 #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO)
17795 #define MA_NO_RUNTIME_LINKING
17796 #endif
17797 #endif
17798
17799 #ifndef MA_NO_DEVICE_IO
17800
17801 #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
17802 #include <mach/mach_time.h> /* For mach_absolute_time() */
17803 #endif
17804
17805 #ifdef MA_POSIX
17806 #include <sys/types.h>
17807 #include <unistd.h>
17808
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
17816
17817 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)
17818 {
17819 if (pDeviceInfo == NULL) {
17820 return;
17821 }
17822
17823 if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {
17824 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
17825 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
17826 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
17827 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
17828 pDeviceInfo->nativeDataFormatCount += 1;
17829 }
17830 }
17831
17832
17833 typedef struct
17834 {
17835 ma_backend backend;
17836 const char* pName;
17837 } ma_backend_info;
17838
17839 static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */
17840 {
17841 {ma_backend_wasapi, "WASAPI"},
17842 {ma_backend_dsound, "DirectSound"},
17843 {ma_backend_winmm, "WinMM"},
17844 {ma_backend_coreaudio, "Core Audio"},
17845 {ma_backend_sndio, "sndio"},
17846 {ma_backend_audio4, "audio(4)"},
17847 {ma_backend_oss, "OSS"},
17848 {ma_backend_pulseaudio, "PulseAudio"},
17849 {ma_backend_alsa, "ALSA"},
17850 {ma_backend_jack, "JACK"},
17851 {ma_backend_aaudio, "AAudio"},
17852 {ma_backend_opensl, "OpenSL|ES"},
17853 {ma_backend_webaudio, "Web Audio"},
17854 {ma_backend_custom, "Custom"},
17855 {ma_backend_null, "Null"}
17856 };
17857
17858 MA_API const char* ma_get_backend_name(ma_backend backend)
17859 {
17860 if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) {
17861 return "Unknown";
17862 }
17863
17864 return gBackendInfo[backend].pName;
17865 }
17866
17867 MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend)
17868 {
17869 size_t iBackend;
17870
17871 if (pBackendName == NULL) {
17872 return MA_INVALID_ARGS;
17873 }
17874
17875 for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) {
17876 if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) {
17877 if (pBackend != NULL) {
17878 *pBackend = gBackendInfo[iBackend].backend;
17879 }
17880
17881 return MA_SUCCESS;
17882 }
17883 }
17884
17885 /* Getting here means the backend name is unknown. */
17886 return MA_INVALID_ARGS;
17887 }
17888
17889 MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
17890 {
17891 /*
17892 This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers
17893 about some enums not being handled by the switch statement.
17894 */
17895 switch (backend)
17896 {
17897 case ma_backend_wasapi:
17898 #if defined(MA_HAS_WASAPI)
17899 return MA_TRUE;
17900 #else
17901 return MA_FALSE;
17902 #endif
17903 case ma_backend_dsound:
17904 #if defined(MA_HAS_DSOUND)
17905 return MA_TRUE;
17906 #else
17907 return MA_FALSE;
17908 #endif
17909 case ma_backend_winmm:
17910 #if defined(MA_HAS_WINMM)
17911 return MA_TRUE;
17912 #else
17913 return MA_FALSE;
17914 #endif
17915 case ma_backend_coreaudio:
17916 #if defined(MA_HAS_COREAUDIO)
17917 return MA_TRUE;
17918 #else
17919 return MA_FALSE;
17920 #endif
17921 case ma_backend_sndio:
17922 #if defined(MA_HAS_SNDIO)
17923 return MA_TRUE;
17924 #else
17925 return MA_FALSE;
17926 #endif
17927 case ma_backend_audio4:
17928 #if defined(MA_HAS_AUDIO4)
17929 return MA_TRUE;
17930 #else
17931 return MA_FALSE;
17932 #endif
17933 case ma_backend_oss:
17934 #if defined(MA_HAS_OSS)
17935 return MA_TRUE;
17936 #else
17937 return MA_FALSE;
17938 #endif
17939 case ma_backend_pulseaudio:
17940 #if defined(MA_HAS_PULSEAUDIO)
17941 return MA_TRUE;
17942 #else
17943 return MA_FALSE;
17944 #endif
17945 case ma_backend_alsa:
17946 #if defined(MA_HAS_ALSA)
17947 return MA_TRUE;
17948 #else
17949 return MA_FALSE;
17950 #endif
17951 case ma_backend_jack:
17952 #if defined(MA_HAS_JACK)
17953 return MA_TRUE;
17954 #else
17955 return MA_FALSE;
17956 #endif
17957 case ma_backend_aaudio:
17958 #if defined(MA_HAS_AAUDIO)
17959 #if defined(MA_ANDROID)
17960 {
17961 return ma_android_sdk_version() >= 26;
17962 }
17963 #else
17964 return MA_FALSE;
17965 #endif
17966 #else
17967 return MA_FALSE;
17968 #endif
17969 case ma_backend_opensl:
17970 #if defined(MA_HAS_OPENSL)
17971 #if defined(MA_ANDROID)
17972 {
17973 return ma_android_sdk_version() >= 9;
17974 }
17975 #else
17976 return MA_TRUE;
17977 #endif
17978 #else
17979 return MA_FALSE;
17980 #endif
17981 case ma_backend_webaudio:
17982 #if defined(MA_HAS_WEBAUDIO)
17983 return MA_TRUE;
17984 #else
17985 return MA_FALSE;
17986 #endif
17987 case ma_backend_custom:
17988 #if defined(MA_HAS_CUSTOM)
17989 return MA_TRUE;
17990 #else
17991 return MA_FALSE;
17992 #endif
17993 case ma_backend_null:
17994 #if defined(MA_HAS_NULL)
17995 return MA_TRUE;
17996 #else
17997 return MA_FALSE;
17998 #endif
17999
18000 default: return MA_FALSE;
18001 }
18002 }
18003
18004 MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount)
18005 {
18006 size_t backendCount;
18007 size_t iBackend;
18008 ma_result result = MA_SUCCESS;
18009
18010 if (pBackendCount == NULL) {
18011 return MA_INVALID_ARGS;
18012 }
18013
18014 backendCount = 0;
18015
18016 for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) {
18017 ma_backend backend = (ma_backend)iBackend;
18018
18019 if (ma_is_backend_enabled(backend)) {
18020 /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */
18021 if (backendCount == backendCap) {
18022 result = MA_NO_SPACE;
18023 break;
18024 } else {
18025 pBackends[backendCount] = backend;
18026 backendCount += 1;
18027 }
18028 }
18029 }
18030
18031 if (pBackendCount != NULL) {
18032 *pBackendCount = backendCount;
18033 }
18034
18035 return result;
18036 }
18037
18038 MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
18039 {
18040 switch (backend)
18041 {
18042 case ma_backend_wasapi: return MA_TRUE;
18043 case ma_backend_dsound: return MA_FALSE;
18044 case ma_backend_winmm: return MA_FALSE;
18045 case ma_backend_coreaudio: return MA_FALSE;
18046 case ma_backend_sndio: return MA_FALSE;
18047 case ma_backend_audio4: return MA_FALSE;
18048 case ma_backend_oss: return MA_FALSE;
18049 case ma_backend_pulseaudio: return MA_FALSE;
18050 case ma_backend_alsa: return MA_FALSE;
18051 case ma_backend_jack: return MA_FALSE;
18052 case ma_backend_aaudio: return MA_FALSE;
18053 case ma_backend_opensl: return MA_FALSE;
18054 case ma_backend_webaudio: return MA_FALSE;
18055 case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
18056 case ma_backend_null: return MA_FALSE;
18057 default: return MA_FALSE;
18058 }
18059 }
18060
18061
18062
18063 #if defined(MA_WIN32)
18064 /* WASAPI error codes. */
18065 #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
18066 #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
18067 #define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
18068 #define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
18069 #define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
18070 #define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
18071 #define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
18072 #define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
18073 #define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
18074 #define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
18075 #define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
18076 #define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
18077 #define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
18078 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
18079 #define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
18080 #define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
18081 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
18082 #define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
18083 #define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
18084 #define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
18085 #define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
18086 #define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
18087 #define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
18088 #define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
18089 #define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
18090 #define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
18091 #define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
18092 #define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
18093 #define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
18094 #define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
18095 #define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
18096 #define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
18097 #define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
18098 #define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
18099 #define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
18100 #define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
18101 #define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
18102 #define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
18103 #define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
18104 #define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
18105
18106 #define MA_DS_OK ((HRESULT)0)
18107 #define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
18108 #define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
18109 #define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
18110 #define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
18111 #define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
18112 #define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
18113 #define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
18114 #define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
18115 #define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
18116 #define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
18117 #define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
18118 #define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
18119 #define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
18120 #define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
18121 #define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
18122 #define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
18123 #define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
18124 #define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
18125 #define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
18126 #define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
18127 #define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
18128 #define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
18129 #define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
18130 #define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
18131
18132 static ma_result ma_result_from_HRESULT(HRESULT hr)
18133 {
18134 switch (hr)
18135 {
18136 case NOERROR: return MA_SUCCESS;
18137 /*case S_OK: return MA_SUCCESS;*/
18138
18139 case E_POINTER: return MA_INVALID_ARGS;
18140 case E_UNEXPECTED: return MA_ERROR;
18141 case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
18142 case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
18143 case E_INVALIDARG: return MA_INVALID_ARGS;
18144 case E_NOINTERFACE: return MA_API_NOT_FOUND;
18145 case E_HANDLE: return MA_INVALID_ARGS;
18146 case E_ABORT: return MA_ERROR;
18147 case E_FAIL: return MA_ERROR;
18148 case E_ACCESSDENIED: return MA_ACCESS_DENIED;
18149
18150 /* WASAPI */
18151 case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
18152 case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
18153 case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
18154 case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
18155 case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
18156 case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
18157 case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
18158 case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
18159 case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
18160 case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
18161 case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
18162 case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
18163 case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
18164 case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
18165 case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
18166 case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
18167 case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
18168 case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
18169 case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
18170 case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
18171 case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
18172 case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
18173 case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
18174 case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
18175 case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
18176 case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
18177 case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
18178 case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
18179 case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
18180 case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
18181 case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
18182 case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
18183 case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
18184 case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
18185 case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
18186 case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
18187 case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
18188 case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
18189 case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
18190 case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
18191
18192 /* DirectSound */
18193 /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */
18194 case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
18195 case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
18196 case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
18197 /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
18198 case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
18199 /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
18200 case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
18201 /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
18202 case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
18203 /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
18204 case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
18205 case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
18206 case MA_DSERR_NOAGGREGATION: return MA_ERROR;
18207 case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
18208 case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
18209 case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
18210 /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
18211 /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
18212 case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
18213 case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
18214 case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
18215 case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
18216 case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
18217 case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
18218
18219 default: return MA_ERROR;
18220 }
18221 }
18222
18223 /* PROPVARIANT */
18224 #define MA_VT_LPWSTR 31
18225 #define MA_VT_BLOB 65
18226
18227 #if defined(_MSC_VER) && !defined(__clang__)
18228 #pragma warning(push)
18229 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
18230 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
18231 #pragma GCC diagnostic push
18232 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
18233 #if defined(__clang__)
18234 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
18235 #endif
18236 #endif
18237 typedef struct
18238 {
18239 WORD vt;
18240 WORD wReserved1;
18241 WORD wReserved2;
18242 WORD wReserved3;
18243 union
18244 {
18245 struct
18246 {
18247 ULONG cbSize;
18248 BYTE* pBlobData;
18249 } blob;
18250 WCHAR* pwszVal;
18251 char pad[16]; /* Just to ensure the size of the struct matches the official version. */
18252 };
18253 } MA_PROPVARIANT;
18254 #if defined(_MSC_VER) && !defined(__clang__)
18255 #pragma warning(pop)
18256 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
18257 #pragma GCC diagnostic pop
18258 #endif
18259
18260 typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved);
18261 typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit);
18262 typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
18263 typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv);
18264 typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv);
18265 typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar);
18266 typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax);
18267
18268 typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
18269 typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
18270
18271 #if defined(MA_WIN32_DESKTOP)
18272 /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
18273 typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult);
18274 typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
18275 typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData);
18276 #endif /* MA_WIN32_DESKTOP */
18277
18278
18279 MA_API size_t ma_strlen_WCHAR(const WCHAR* str)
18280 {
18281 size_t len = 0;
18282 while (str[len] != '\0') {
18283 len += 1;
18284 }
18285
18286 return len;
18287 }
18288
18289 MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2)
18290 {
18291 while (*s1 != '\0' && *s1 == *s2) {
18292 s1 += 1;
18293 s2 += 1;
18294 }
18295
18296 return *s1 - *s2;
18297 }
18298
18299 MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src)
18300 {
18301 size_t i;
18302
18303 if (dst == 0) {
18304 return 22;
18305 }
18306 if (dstCap == 0) {
18307 return 34;
18308 }
18309 if (src == 0) {
18310 dst[0] = '\0';
18311 return 22;
18312 }
18313
18314 for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
18315 dst[i] = src[i];
18316 }
18317
18318 if (i < dstCap) {
18319 dst[i] = '\0';
18320 return 0;
18321 }
18322
18323 dst[0] = '\0';
18324 return 34;
18325 }
18326 #endif /* MA_WIN32 */
18327
18328
18329 #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
18330 #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
18331
18332
18333
18334
18335 /*******************************************************************************
18336
18337 Timing
18338
18339 *******************************************************************************/
18340 #if defined(MA_WIN32) && !defined(MA_POSIX)
18341 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */
18342 void ma_timer_init(ma_timer* pTimer)
18343 {
18344 LARGE_INTEGER counter;
18345
18346 if (g_ma_TimerFrequency.QuadPart == 0) {
18347 QueryPerformanceFrequency(&g_ma_TimerFrequency);
18348 }
18349
18350 QueryPerformanceCounter(&counter);
18351 pTimer->counter = counter.QuadPart;
18352 }
18353
18354 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
18355 {
18356 LARGE_INTEGER counter;
18357 if (!QueryPerformanceCounter(&counter)) {
18358 return 0;
18359 }
18360
18361 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
18362 }
18363 #elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
18364 static ma_uint64 g_ma_TimerFrequency = 0;
18365 static void ma_timer_init(ma_timer* pTimer)
18366 {
18367 mach_timebase_info_data_t baseTime;
18368 mach_timebase_info(&baseTime);
18369 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
18370
18371 pTimer->counter = mach_absolute_time();
18372 }
18373
18374 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
18375 {
18376 ma_uint64 newTimeCounter = mach_absolute_time();
18377 ma_uint64 oldTimeCounter = pTimer->counter;
18378
18379 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
18380 }
18381 #elif defined(MA_EMSCRIPTEN)
18382 static MA_INLINE void ma_timer_init(ma_timer* pTimer)
18383 {
18384 pTimer->counterD = emscripten_get_now();
18385 }
18386
18387 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
18388 {
18389 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
18390 }
18391 #else
18392 #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
18393 #if defined(CLOCK_MONOTONIC)
18394 #define MA_CLOCK_ID CLOCK_MONOTONIC
18395 #else
18396 #define MA_CLOCK_ID CLOCK_REALTIME
18397 #endif
18398
18399 static void ma_timer_init(ma_timer* pTimer)
18400 {
18401 struct timespec newTime;
18402 clock_gettime(MA_CLOCK_ID, &newTime);
18403
18404 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
18405 }
18406
18407 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
18408 {
18409 ma_uint64 newTimeCounter;
18410 ma_uint64 oldTimeCounter;
18411
18412 struct timespec newTime;
18413 clock_gettime(MA_CLOCK_ID, &newTime);
18414
18415 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
18416 oldTimeCounter = pTimer->counter;
18417
18418 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
18419 }
18420 #else
18421 static void ma_timer_init(ma_timer* pTimer)
18422 {
18423 struct timeval newTime;
18424 gettimeofday(&newTime, NULL);
18425
18426 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
18427 }
18428
18429 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
18430 {
18431 ma_uint64 newTimeCounter;
18432 ma_uint64 oldTimeCounter;
18433
18434 struct timeval newTime;
18435 gettimeofday(&newTime, NULL);
18436
18437 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
18438 oldTimeCounter = pTimer->counter;
18439
18440 return (newTimeCounter - oldTimeCounter) / 1000000.0;
18441 }
18442 #endif
18443 #endif
18444
18445
18446 /*******************************************************************************
18447
18448 Dynamic Linking
18449
18450 *******************************************************************************/
18451 MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
18452 {
18453 #ifndef MA_NO_RUNTIME_LINKING
18454 ma_handle handle;
18455
18456 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename);
18457
18458 #ifdef MA_WIN32
18459 /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/
18460 #if !defined(MA_WIN32_UWP)
18461 handle = (ma_handle)LoadLibraryA(filename);
18462 #else
18463 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
18464 WCHAR filenameW[4096];
18465 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
18466 handle = NULL;
18467 } else {
18468 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
18469 }
18470 #endif
18471 #else
18472 handle = (ma_handle)dlopen(filename, RTLD_NOW);
18473 #endif
18474
18475 /*
18476 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
18477 backend is a deliberate design choice. Instead I'm logging it as an informational message.
18478 */
18479 if (handle == NULL) {
18480 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename);
18481 }
18482
18483 (void)pContext; /* It's possible for pContext to be unused. */
18484 return handle;
18485 #else
18486 /* Runtime linking is disabled. */
18487 (void)pContext;
18488 (void)filename;
18489 return NULL;
18490 #endif
18491 }
18492
18493 MA_API void ma_dlclose(ma_context* pContext, ma_handle handle)
18494 {
18495 #ifndef MA_NO_RUNTIME_LINKING
18496 #ifdef MA_WIN32
18497 FreeLibrary((HMODULE)handle);
18498 #else
18499 dlclose((void*)handle);
18500 #endif
18501
18502 (void)pContext;
18503 #else
18504 /* Runtime linking is disabled. */
18505 (void)pContext;
18506 (void)handle;
18507 #endif
18508 }
18509
18510 MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
18511 {
18512 #ifndef MA_NO_RUNTIME_LINKING
18513 ma_proc proc;
18514
18515 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol);
18516
18517 #ifdef _WIN32
18518 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
18519 #else
18520 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
18521 #pragma GCC diagnostic push
18522 #pragma GCC diagnostic ignored "-Wpedantic"
18523 #endif
18524 proc = (ma_proc)dlsym((void*)handle, symbol);
18525 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
18526 #pragma GCC diagnostic pop
18527 #endif
18528 #endif
18529
18530 if (proc == NULL) {
18531 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol);
18532 }
18533
18534 (void)pContext; /* It's possible for pContext to be unused. */
18535 return proc;
18536 #else
18537 /* Runtime linking is disabled. */
18538 (void)pContext;
18539 (void)handle;
18540 (void)symbol;
18541 return NULL;
18542 #endif
18543 }
18544
18545
18546 #if 0
18547 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
18548 {
18549 ma_uint32 closestRate = 0;
18550 ma_uint32 closestDiff = 0xFFFFFFFF;
18551 size_t iStandardRate;
18552
18553 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
18554 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
18555 ma_uint32 diff;
18556
18557 if (sampleRateIn > standardRate) {
18558 diff = sampleRateIn - standardRate;
18559 } else {
18560 diff = standardRate - sampleRateIn;
18561 }
18562
18563 if (diff == 0) {
18564 return standardRate; /* The input sample rate is a standard rate. */
18565 }
18566
18567 if (closestDiff > diff) {
18568 closestDiff = diff;
18569 closestRate = standardRate;
18570 }
18571 }
18572
18573 return closestRate;
18574 }
18575 #endif
18576
18577
18578 static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice)
18579 {
18580 MA_ASSERT(pDevice != NULL);
18581
18582 if (!pDevice->noDisableDenormals) {
18583 return ma_disable_denormals();
18584 } else {
18585 return 0;
18586 }
18587 }
18588
18589 static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState)
18590 {
18591 MA_ASSERT(pDevice != NULL);
18592
18593 if (!pDevice->noDisableDenormals) {
18594 ma_restore_denormals(prevState);
18595 } else {
18596 /* Do nothing. */
18597 (void)prevState;
18598 }
18599 }
18600
18601 static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type)
18602 {
18603 ma_device_notification notification;
18604
18605 MA_ZERO_OBJECT(&notification);
18606 notification.pDevice = pDevice;
18607 notification.type = type;
18608
18609 return notification;
18610 }
18611
18612 static void ma_device__on_notification(ma_device_notification notification)
18613 {
18614 MA_ASSERT(notification.pDevice != NULL);
18615
18616 if (notification.pDevice->onNotification != NULL) {
18617 notification.pDevice->onNotification(&notification);
18618 }
18619
18620 /* 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. */
18621 if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) {
18622 notification.pDevice->onStop(notification.pDevice);
18623 }
18624 }
18625
18626 void ma_device__on_notification_started(ma_device* pDevice)
18627 {
18628 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started));
18629 }
18630
18631 void ma_device__on_notification_stopped(ma_device* pDevice)
18632 {
18633 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped));
18634 }
18635
18636 void ma_device__on_notification_rerouted(ma_device* pDevice)
18637 {
18638 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted));
18639 }
18640
18641 void ma_device__on_notification_interruption_began(ma_device* pDevice)
18642 {
18643 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began));
18644 }
18645
18646 void ma_device__on_notification_interruption_ended(ma_device* pDevice)
18647 {
18648 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended));
18649 }
18650
18651
18652 static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
18653 {
18654 MA_ASSERT(pDevice != NULL);
18655 MA_ASSERT(pDevice->onData != NULL);
18656
18657 if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) {
18658 ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
18659 }
18660
18661 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
18662 }
18663
18664 static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
18665 {
18666 MA_ASSERT(pDevice != NULL);
18667
18668 /* Don't read more data from the client if we're in the process of stopping. */
18669 if (ma_device_get_state(pDevice) == ma_device_state_stopping) {
18670 return;
18671 }
18672
18673 if (pDevice->noFixedSizedCallback) {
18674 /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */
18675 ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount);
18676 } else {
18677 /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */
18678 ma_uint32 totalFramesProcessed = 0;
18679
18680 while (totalFramesProcessed < frameCount) {
18681 ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed;
18682 ma_uint32 framesToProcessThisIteration = 0;
18683
18684 if (pFramesIn != NULL) {
18685 /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */
18686 if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) {
18687 /* There's some room left in the intermediary buffer. Write to it without firing the callback. */
18688 framesToProcessThisIteration = totalFramesRemaining;
18689 if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) {
18690 framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen;
18691 }
18692
18693 ma_copy_pcm_frames(
18694 ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels),
18695 ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels),
18696 framesToProcessThisIteration,
18697 pDevice->capture.format, pDevice->capture.channels);
18698
18699 pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration;
18700 }
18701
18702 if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) {
18703 /* No room left in the intermediary buffer. Fire the data callback. */
18704 if (pDevice->type == ma_device_type_duplex) {
18705 /* We'll do the duplex data callback later after we've processed the playback data. */
18706 } else {
18707 ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
18708
18709 /* The intermediary buffer has just been drained. */
18710 pDevice->capture.intermediaryBufferLen = 0;
18711 }
18712 }
18713 }
18714
18715 if (pFramesOut != NULL) {
18716 /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */
18717 if (pDevice->playback.intermediaryBufferLen > 0) {
18718 /* There's some content in the intermediary buffer. Read from that without firing the callback. */
18719 if (pDevice->type == ma_device_type_duplex) {
18720 /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */
18721 } else {
18722 framesToProcessThisIteration = totalFramesRemaining;
18723 if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) {
18724 framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen;
18725 }
18726 }
18727
18728 ma_copy_pcm_frames(
18729 ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels),
18730 ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels),
18731 framesToProcessThisIteration,
18732 pDevice->playback.format, pDevice->playback.channels);
18733
18734 pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration;
18735 }
18736
18737 if (pDevice->playback.intermediaryBufferLen == 0) {
18738 /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */
18739 if (pDevice->type == ma_device_type_duplex) {
18740 /* In duplex mode, the data callback will be fired later. Nothing to do here. */
18741 } else {
18742 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap);
18743
18744 /* The intermediary buffer has just been filled. */
18745 pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap;
18746 }
18747 }
18748 }
18749
18750 /* If we're in duplex mode we might need to do a refill of the data. */
18751 if (pDevice->type == ma_device_type_duplex) {
18752 if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) {
18753 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
18754
18755 pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */
18756 pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */
18757 }
18758 }
18759
18760 /* Make sure this is only incremented once in the duplex case. */
18761 totalFramesProcessed += framesToProcessThisIteration;
18762 }
18763 }
18764 }
18765
18766 static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
18767 {
18768 float masterVolumeFactor;
18769
18770 ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */
18771
18772 if (pDevice->onData) {
18773 unsigned int prevDenormalState = ma_device_disable_denormals(pDevice);
18774 {
18775 /* 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. */
18776 if (pFramesIn != NULL && masterVolumeFactor < 1) {
18777 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18778 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18779 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
18780 ma_uint32 totalFramesProcessed = 0;
18781 while (totalFramesProcessed < frameCount) {
18782 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
18783 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
18784 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
18785 }
18786
18787 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
18788
18789 ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
18790
18791 totalFramesProcessed += framesToProcessThisIteration;
18792 }
18793 } else {
18794 ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount);
18795 }
18796
18797 /* Volume control and clipping for playback devices. */
18798 if (pFramesOut != NULL) {
18799 if (masterVolumeFactor < 1) {
18800 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. */
18801 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
18802 }
18803 }
18804
18805 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
18806 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. */
18807 }
18808 }
18809 }
18810 ma_device_restore_denormals(pDevice, prevDenormalState);
18811 }
18812 }
18813
18814
18815
18816 /* A helper function for reading sample data from the client. */
18817 static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
18818 {
18819 MA_ASSERT(pDevice != NULL);
18820 MA_ASSERT(frameCount > 0);
18821 MA_ASSERT(pFramesOut != NULL);
18822
18823 if (pDevice->playback.converter.isPassthrough) {
18824 ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount);
18825 } else {
18826 ma_result result;
18827 ma_uint64 totalFramesReadOut;
18828 void* pRunningFramesOut;
18829
18830 totalFramesReadOut = 0;
18831 pRunningFramesOut = pFramesOut;
18832
18833 /*
18834 We run slightly different logic depending on whether or not we're using a heap-allocated
18835 buffer for caching input data. This will be the case if the data converter does not have
18836 the ability to retrieve the required input frame count for a given output frame count.
18837 */
18838 if (pDevice->playback.pInputCache != NULL) {
18839 while (totalFramesReadOut < frameCount) {
18840 ma_uint64 framesToReadThisIterationIn;
18841 ma_uint64 framesToReadThisIterationOut;
18842
18843 /* If there's any data available in the cache, that needs to get processed first. */
18844 if (pDevice->playback.inputCacheRemaining > 0) {
18845 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
18846 framesToReadThisIterationIn = framesToReadThisIterationOut;
18847 if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) {
18848 framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining;
18849 }
18850
18851 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);
18852 if (result != MA_SUCCESS) {
18853 break;
18854 }
18855
18856 pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn;
18857 pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn;
18858
18859 totalFramesReadOut += framesToReadThisIterationOut;
18860 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
18861
18862 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
18863 break; /* We're done. */
18864 }
18865 }
18866
18867 /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */
18868 if (pDevice->playback.inputCacheRemaining == 0) {
18869 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap);
18870
18871 pDevice->playback.inputCacheConsumed = 0;
18872 pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap;
18873 }
18874 }
18875 } else {
18876 while (totalFramesReadOut < frameCount) {
18877 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */
18878 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
18879 ma_uint64 framesToReadThisIterationIn;
18880 ma_uint64 framesReadThisIterationIn;
18881 ma_uint64 framesToReadThisIterationOut;
18882 ma_uint64 framesReadThisIterationOut;
18883 ma_uint64 requiredInputFrameCount;
18884
18885 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
18886 framesToReadThisIterationIn = framesToReadThisIterationOut;
18887 if (framesToReadThisIterationIn > intermediaryBufferCap) {
18888 framesToReadThisIterationIn = intermediaryBufferCap;
18889 }
18890
18891 ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount);
18892 if (framesToReadThisIterationIn > requiredInputFrameCount) {
18893 framesToReadThisIterationIn = requiredInputFrameCount;
18894 }
18895
18896 if (framesToReadThisIterationIn > 0) {
18897 ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
18898 }
18899
18900 /*
18901 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
18902 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
18903 */
18904 framesReadThisIterationIn = framesToReadThisIterationIn;
18905 framesReadThisIterationOut = framesToReadThisIterationOut;
18906 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
18907 if (result != MA_SUCCESS) {
18908 break;
18909 }
18910
18911 totalFramesReadOut += framesReadThisIterationOut;
18912 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
18913
18914 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
18915 break; /* We're done. */
18916 }
18917 }
18918 }
18919 }
18920 }
18921
18922 /* A helper for sending sample data to the client. */
18923 static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
18924 {
18925 MA_ASSERT(pDevice != NULL);
18926 MA_ASSERT(frameCountInDeviceFormat > 0);
18927 MA_ASSERT(pFramesInDeviceFormat != NULL);
18928
18929 if (pDevice->capture.converter.isPassthrough) {
18930 ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
18931 } else {
18932 ma_result result;
18933 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
18934 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18935 ma_uint64 totalDeviceFramesProcessed = 0;
18936 ma_uint64 totalClientFramesProcessed = 0;
18937 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
18938
18939 /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */
18940 for (;;) {
18941 ma_uint64 deviceFramesProcessedThisIteration;
18942 ma_uint64 clientFramesProcessedThisIteration;
18943
18944 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
18945 clientFramesProcessedThisIteration = framesInClientFormatCap;
18946
18947 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
18948 if (result != MA_SUCCESS) {
18949 break;
18950 }
18951
18952 if (clientFramesProcessedThisIteration > 0) {
18953 ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */
18954 }
18955
18956 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
18957 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
18958 totalClientFramesProcessed += clientFramesProcessedThisIteration;
18959
18960 /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */
18961 (void)totalClientFramesProcessed;
18962
18963 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
18964 break; /* We're done. */
18965 }
18966 }
18967 }
18968 }
18969
18970 static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
18971 {
18972 ma_result result;
18973 ma_uint32 totalDeviceFramesProcessed = 0;
18974 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
18975
18976 MA_ASSERT(pDevice != NULL);
18977 MA_ASSERT(frameCountInDeviceFormat > 0);
18978 MA_ASSERT(pFramesInDeviceFormat != NULL);
18979 MA_ASSERT(pRB != NULL);
18980
18981 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
18982 for (;;) {
18983 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
18984 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
18985 ma_uint64 framesProcessedInDeviceFormat;
18986 ma_uint64 framesProcessedInClientFormat;
18987 void* pFramesInClientFormat;
18988
18989 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
18990 if (result != MA_SUCCESS) {
18991 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.");
18992 break;
18993 }
18994
18995 if (framesToProcessInClientFormat == 0) {
18996 if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {
18997 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
18998 }
18999 }
19000
19001 /* Convert. */
19002 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
19003 framesProcessedInClientFormat = framesToProcessInClientFormat;
19004 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
19005 if (result != MA_SUCCESS) {
19006 break;
19007 }
19008
19009 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */
19010 if (result != MA_SUCCESS) {
19011 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.");
19012 break;
19013 }
19014
19015 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
19016 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
19017
19018 /* We're done when we're unable to process any client nor device frames. */
19019 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
19020 break; /* Done. */
19021 }
19022 }
19023
19024 return MA_SUCCESS;
19025 }
19026
19027 static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
19028 {
19029 ma_result result;
19030 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
19031 ma_uint32 totalFramesReadOut = 0;
19032
19033 MA_ASSERT(pDevice != NULL);
19034 MA_ASSERT(frameCount > 0);
19035 MA_ASSERT(pFramesInInternalFormat != NULL);
19036 MA_ASSERT(pRB != NULL);
19037 MA_ASSERT(pDevice->playback.pInputCache != NULL);
19038
19039 /*
19040 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
19041 the whole frameCount frames we just use silence instead for the input data.
19042 */
19043 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
19044
19045 while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) {
19046 /*
19047 We should have a buffer allocated on the heap. Any playback frames still sitting in there
19048 need to be sent to the internal device before we process any more data from the client.
19049 */
19050 if (pDevice->playback.inputCacheRemaining > 0) {
19051 ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining;
19052 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
19053 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);
19054
19055 pDevice->playback.inputCacheConsumed += framesConvertedIn;
19056 pDevice->playback.inputCacheRemaining -= framesConvertedIn;
19057
19058 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
19059 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
19060 }
19061
19062 /* If there's no more data in the cache we'll need to fill it with some. */
19063 if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) {
19064 ma_uint32 inputFrameCount;
19065 void* pInputFrames;
19066
19067 inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap;
19068 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
19069 if (result == MA_SUCCESS) {
19070 if (inputFrameCount > 0) {
19071 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount);
19072 } else {
19073 if (ma_pcm_rb_pointer_distance(pRB) == 0) {
19074 break; /* Underrun. */
19075 }
19076 }
19077 } else {
19078 /* No capture data available. Feed in silence. */
19079 inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
19080 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount);
19081 }
19082
19083 pDevice->playback.inputCacheConsumed = 0;
19084 pDevice->playback.inputCacheRemaining = inputFrameCount;
19085
19086 result = ma_pcm_rb_commit_read(pRB, inputFrameCount);
19087 if (result != MA_SUCCESS) {
19088 return result; /* Should never happen. */
19089 }
19090 }
19091 }
19092
19093 return MA_SUCCESS;
19094 }
19095
19096 /* A helper for changing the state of the device. */
19097 static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState)
19098 {
19099 ma_atomic_device_state_set(&pDevice->state, newState);
19100 }
19101
19102
19103 #if defined(MA_WIN32)
19104 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
19105 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
19106 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
19107 /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
19108 #endif
19109
19110
19111
19112 MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
19113 {
19114 ma_uint32 i;
19115 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
19116 if (g_maFormatPriorities[i] == format) {
19117 return i;
19118 }
19119 }
19120
19121 /* Getting here means the format could not be found or is equal to ma_format_unknown. */
19122 return (ma_uint32)-1;
19123 }
19124
19125 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
19126
19127 static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)
19128 {
19129 if (pDeviceDescriptor == NULL) {
19130 return MA_FALSE;
19131 }
19132
19133 if (pDeviceDescriptor->format == ma_format_unknown) {
19134 return MA_FALSE;
19135 }
19136
19137 if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {
19138 return MA_FALSE;
19139 }
19140
19141 if (pDeviceDescriptor->sampleRate == 0) {
19142 return MA_FALSE;
19143 }
19144
19145 return MA_TRUE;
19146 }
19147
19148
19149 static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
19150 {
19151 ma_result result = MA_SUCCESS;
19152 ma_bool32 exitLoop = MA_FALSE;
19153 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
19154 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
19155 ma_uint32 capturedDeviceDataCapInFrames = 0;
19156 ma_uint32 playbackDeviceDataCapInFrames = 0;
19157
19158 MA_ASSERT(pDevice != NULL);
19159
19160 /* Just some quick validation on the device type and the available callbacks. */
19161 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
19162 if (pDevice->pContext->callbacks.onDeviceRead == NULL) {
19163 return MA_NOT_IMPLEMENTED;
19164 }
19165
19166 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
19167 }
19168
19169 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
19170 if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {
19171 return MA_NOT_IMPLEMENTED;
19172 }
19173
19174 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
19175 }
19176
19177 /* NOTE: The device was started outside of this function, in the worker thread. */
19178
19179 while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) {
19180 switch (pDevice->type) {
19181 case ma_device_type_duplex:
19182 {
19183 /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */
19184 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
19185 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
19186
19187 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
19188 ma_uint32 capturedDeviceFramesRemaining;
19189 ma_uint32 capturedDeviceFramesProcessed;
19190 ma_uint32 capturedDeviceFramesToProcess;
19191 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
19192 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
19193 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
19194 }
19195
19196 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
19197 if (result != MA_SUCCESS) {
19198 exitLoop = MA_TRUE;
19199 break;
19200 }
19201
19202 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
19203 capturedDeviceFramesProcessed = 0;
19204
19205 /* At this point we have our captured data in device format and we now need to convert it to client format. */
19206 for (;;) {
19207 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
19208 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
19209 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
19210 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
19211 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
19212 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
19213 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
19214
19215 /* Convert capture data from device format to client format. */
19216 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
19217 if (result != MA_SUCCESS) {
19218 break;
19219 }
19220
19221 /*
19222 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
19223 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
19224 */
19225 if (capturedClientFramesToProcessThisIteration == 0) {
19226 break;
19227 }
19228
19229 ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
19230
19231 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
19232 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
19233
19234 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
19235 for (;;) {
19236 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
19237 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
19238 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
19239 if (result != MA_SUCCESS) {
19240 break;
19241 }
19242
19243 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
19244 if (result != MA_SUCCESS) {
19245 exitLoop = MA_TRUE;
19246 break;
19247 }
19248
19249 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
19250 if (capturedClientFramesToProcessThisIteration == 0) {
19251 break;
19252 }
19253 }
19254
19255 /* In case an error happened from ma_device_write__null()... */
19256 if (result != MA_SUCCESS) {
19257 exitLoop = MA_TRUE;
19258 break;
19259 }
19260 }
19261
19262 /* Make sure we don't get stuck in the inner loop. */
19263 if (capturedDeviceFramesProcessed == 0) {
19264 break;
19265 }
19266
19267 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
19268 }
19269 } break;
19270
19271 case ma_device_type_capture:
19272 case ma_device_type_loopback:
19273 {
19274 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
19275 ma_uint32 framesReadThisPeriod = 0;
19276 while (framesReadThisPeriod < periodSizeInFrames) {
19277 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
19278 ma_uint32 framesProcessed;
19279 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
19280 if (framesToReadThisIteration > capturedDeviceDataCapInFrames) {
19281 framesToReadThisIteration = capturedDeviceDataCapInFrames;
19282 }
19283
19284 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed);
19285 if (result != MA_SUCCESS) {
19286 exitLoop = MA_TRUE;
19287 break;
19288 }
19289
19290 /* Make sure we don't get stuck in the inner loop. */
19291 if (framesProcessed == 0) {
19292 break;
19293 }
19294
19295 ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);
19296
19297 framesReadThisPeriod += framesProcessed;
19298 }
19299 } break;
19300
19301 case ma_device_type_playback:
19302 {
19303 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
19304 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
19305 ma_uint32 framesWrittenThisPeriod = 0;
19306 while (framesWrittenThisPeriod < periodSizeInFrames) {
19307 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
19308 ma_uint32 framesProcessed;
19309 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
19310 if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {
19311 framesToWriteThisIteration = playbackDeviceDataCapInFrames;
19312 }
19313
19314 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);
19315
19316 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);
19317 if (result != MA_SUCCESS) {
19318 exitLoop = MA_TRUE;
19319 break;
19320 }
19321
19322 /* Make sure we don't get stuck in the inner loop. */
19323 if (framesProcessed == 0) {
19324 break;
19325 }
19326
19327 framesWrittenThisPeriod += framesProcessed;
19328 }
19329 } break;
19330
19331 /* Should never get here. */
19332 default: break;
19333 }
19334 }
19335
19336 return result;
19337 }
19338
19339
19340
19341 /*******************************************************************************
19342
19343 Null Backend
19344
19345 *******************************************************************************/
19346 #ifdef MA_HAS_NULL
19347
19348 #define MA_DEVICE_OP_NONE__NULL 0
19349 #define MA_DEVICE_OP_START__NULL 1
19350 #define MA_DEVICE_OP_SUSPEND__NULL 2
19351 #define MA_DEVICE_OP_KILL__NULL 3
19352
19353 static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
19354 {
19355 ma_device* pDevice = (ma_device*)pData;
19356 MA_ASSERT(pDevice != NULL);
19357
19358 for (;;) { /* Keep the thread alive until the device is uninitialized. */
19359 ma_uint32 operation;
19360
19361 /* Wait for an operation to be requested. */
19362 ma_event_wait(&pDevice->null_device.operationEvent);
19363
19364 /* At this point an event should have been triggered. */
19365 operation = pDevice->null_device.operation;
19366
19367 /* Starting the device needs to put the thread into a loop. */
19368 if (operation == MA_DEVICE_OP_START__NULL) {
19369 /* Reset the timer just in case. */
19370 ma_timer_init(&pDevice->null_device.timer);
19371
19372 /* Getting here means a suspend or kill operation has been requested. */
19373 pDevice->null_device.operationResult = MA_SUCCESS;
19374 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
19375 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
19376 continue;
19377 }
19378
19379 /* Suspending the device means we need to stop the timer and just continue the loop. */
19380 if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
19381 /* We need to add the current run time to the prior run time, then reset the timer. */
19382 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
19383 ma_timer_init(&pDevice->null_device.timer);
19384
19385 /* We're done. */
19386 pDevice->null_device.operationResult = MA_SUCCESS;
19387 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
19388 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
19389 continue;
19390 }
19391
19392 /* Killing the device means we need to get out of this loop so that this thread can terminate. */
19393 if (operation == MA_DEVICE_OP_KILL__NULL) {
19394 pDevice->null_device.operationResult = MA_SUCCESS;
19395 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
19396 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
19397 break;
19398 }
19399
19400 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
19401 if (operation == MA_DEVICE_OP_NONE__NULL) {
19402 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). */
19403 pDevice->null_device.operationResult = MA_INVALID_OPERATION;
19404 ma_event_signal(&pDevice->null_device.operationCompletionEvent);
19405 ma_semaphore_release(&pDevice->null_device.operationSemaphore);
19406 continue; /* Continue the loop. Don't terminate. */
19407 }
19408 }
19409
19410 return (ma_thread_result)0;
19411 }
19412
19413 static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
19414 {
19415 ma_result result;
19416
19417 /*
19418 TODO: Need to review this and consider just using mutual exclusion. I think the original motivation
19419 for this was to just post the event to a queue and return immediately, but that has since changed
19420 and now this function is synchronous. I think this can be simplified to just use a mutex.
19421 */
19422
19423 /*
19424 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
19425 to support queing of operations.
19426 */
19427 result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
19428 if (result != MA_SUCCESS) {
19429 return result; /* Failed to wait for the event. */
19430 }
19431
19432 /*
19433 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
19434 signal an event to the worker thread to let it know that it can start work.
19435 */
19436 pDevice->null_device.operation = operation;
19437
19438 /* Once the operation code has been set, the worker thread can start work. */
19439 if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) {
19440 return MA_ERROR;
19441 }
19442
19443 /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */
19444 if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) {
19445 return MA_ERROR;
19446 }
19447
19448 return pDevice->null_device.operationResult;
19449 }
19450
19451 static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
19452 {
19453 ma_uint32 internalSampleRate;
19454 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
19455 internalSampleRate = pDevice->capture.internalSampleRate;
19456 } else {
19457 internalSampleRate = pDevice->playback.internalSampleRate;
19458 }
19459
19460 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
19461 }
19462
19463 static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
19464 {
19465 ma_bool32 cbResult = MA_TRUE;
19466
19467 MA_ASSERT(pContext != NULL);
19468 MA_ASSERT(callback != NULL);
19469
19470 /* Playback. */
19471 if (cbResult) {
19472 ma_device_info deviceInfo;
19473 MA_ZERO_OBJECT(&deviceInfo);
19474 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
19475 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
19476 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
19477 }
19478
19479 /* Capture. */
19480 if (cbResult) {
19481 ma_device_info deviceInfo;
19482 MA_ZERO_OBJECT(&deviceInfo);
19483 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
19484 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
19485 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
19486 }
19487
19488 (void)cbResult; /* Silence a static analysis warning. */
19489
19490 return MA_SUCCESS;
19491 }
19492
19493 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)
19494 {
19495 MA_ASSERT(pContext != NULL);
19496
19497 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
19498 return MA_NO_DEVICE; /* Don't know the device. */
19499 }
19500
19501 /* Name / Description */
19502 if (deviceType == ma_device_type_playback) {
19503 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
19504 } else {
19505 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
19506 }
19507
19508 pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
19509
19510 /* Support everything on the null backend. */
19511 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
19512 pDeviceInfo->nativeDataFormats[0].channels = 0;
19513 pDeviceInfo->nativeDataFormats[0].sampleRate = 0;
19514 pDeviceInfo->nativeDataFormats[0].flags = 0;
19515 pDeviceInfo->nativeDataFormatCount = 1;
19516
19517 (void)pContext;
19518 return MA_SUCCESS;
19519 }
19520
19521
19522 static ma_result ma_device_uninit__null(ma_device* pDevice)
19523 {
19524 MA_ASSERT(pDevice != NULL);
19525
19526 /* Keep it clean and wait for the device thread to finish before returning. */
19527 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
19528
19529 /* Wait for the thread to finish before continuing. */
19530 ma_thread_wait(&pDevice->null_device.deviceThread);
19531
19532 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
19533 ma_semaphore_uninit(&pDevice->null_device.operationSemaphore);
19534 ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
19535 ma_event_uninit(&pDevice->null_device.operationEvent);
19536
19537 return MA_SUCCESS;
19538 }
19539
19540 static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
19541 {
19542 ma_result result;
19543
19544 MA_ASSERT(pDevice != NULL);
19545
19546 MA_ZERO_OBJECT(&pDevice->null_device);
19547
19548 if (pConfig->deviceType == ma_device_type_loopback) {
19549 return MA_DEVICE_TYPE_NOT_SUPPORTED;
19550 }
19551
19552 /* The null backend supports everything exactly as we specify it. */
19553 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
19554 pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT;
19555 pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
19556 pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;
19557
19558 if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {
19559 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
19560 }
19561
19562 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
19563 }
19564
19565 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
19566 pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT;
19567 pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
19568 pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
19569
19570 if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
19571 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);
19572 }
19573
19574 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
19575 }
19576
19577 /*
19578 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
19579 first period is "written" to it, and then stopped in ma_device_stop__null().
19580 */
19581 result = ma_event_init(&pDevice->null_device.operationEvent);
19582 if (result != MA_SUCCESS) {
19583 return result;
19584 }
19585
19586 result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
19587 if (result != MA_SUCCESS) {
19588 return result;
19589 }
19590
19591 result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */
19592 if (result != MA_SUCCESS) {
19593 return result;
19594 }
19595
19596 result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks);
19597 if (result != MA_SUCCESS) {
19598 return result;
19599 }
19600
19601 return MA_SUCCESS;
19602 }
19603
19604 static ma_result ma_device_start__null(ma_device* pDevice)
19605 {
19606 MA_ASSERT(pDevice != NULL);
19607
19608 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
19609
19610 ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE);
19611 return MA_SUCCESS;
19612 }
19613
19614 static ma_result ma_device_stop__null(ma_device* pDevice)
19615 {
19616 MA_ASSERT(pDevice != NULL);
19617
19618 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
19619
19620 ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE);
19621 return MA_SUCCESS;
19622 }
19623
19624 static ma_bool32 ma_device_is_started__null(ma_device* pDevice)
19625 {
19626 MA_ASSERT(pDevice != NULL);
19627
19628 return ma_atomic_bool32_get(&pDevice->null_device.isStarted);
19629 }
19630
19631 static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
19632 {
19633 ma_result result = MA_SUCCESS;
19634 ma_uint32 totalPCMFramesProcessed;
19635 ma_bool32 wasStartedOnEntry;
19636
19637 if (pFramesWritten != NULL) {
19638 *pFramesWritten = 0;
19639 }
19640
19641 wasStartedOnEntry = ma_device_is_started__null(pDevice);
19642
19643 /* Keep going until everything has been read. */
19644 totalPCMFramesProcessed = 0;
19645 while (totalPCMFramesProcessed < frameCount) {
19646 ma_uint64 targetFrame;
19647
19648 /* If there are any frames remaining in the current period, consume those first. */
19649 if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
19650 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
19651 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
19652 if (framesToProcess > framesRemaining) {
19653 framesToProcess = framesRemaining;
19654 }
19655
19656 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
19657 (void)pPCMFrames;
19658
19659 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
19660 totalPCMFramesProcessed += framesToProcess;
19661 }
19662
19663 /* 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. */
19664 if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
19665 pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
19666
19667 if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) {
19668 result = ma_device_start__null(pDevice);
19669 if (result != MA_SUCCESS) {
19670 break;
19671 }
19672 }
19673 }
19674
19675 /* If we've consumed the whole buffer we can return now. */
19676 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
19677 if (totalPCMFramesProcessed == frameCount) {
19678 break;
19679 }
19680
19681 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
19682 targetFrame = pDevice->null_device.lastProcessedFramePlayback;
19683 for (;;) {
19684 ma_uint64 currentFrame;
19685
19686 /* Stop waiting if the device has been stopped. */
19687 if (!ma_device_is_started__null(pDevice)) {
19688 break;
19689 }
19690
19691 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
19692 if (currentFrame >= targetFrame) {
19693 break;
19694 }
19695
19696 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
19697 ma_sleep(10);
19698 }
19699
19700 pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames;
19701 pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames;
19702 }
19703
19704 if (pFramesWritten != NULL) {
19705 *pFramesWritten = totalPCMFramesProcessed;
19706 }
19707
19708 return result;
19709 }
19710
19711 static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
19712 {
19713 ma_result result = MA_SUCCESS;
19714 ma_uint32 totalPCMFramesProcessed;
19715
19716 if (pFramesRead != NULL) {
19717 *pFramesRead = 0;
19718 }
19719
19720 /* Keep going until everything has been read. */
19721 totalPCMFramesProcessed = 0;
19722 while (totalPCMFramesProcessed < frameCount) {
19723 ma_uint64 targetFrame;
19724
19725 /* If there are any frames remaining in the current period, consume those first. */
19726 if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
19727 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
19728 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
19729 ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
19730 if (framesToProcess > framesRemaining) {
19731 framesToProcess = framesRemaining;
19732 }
19733
19734 /* We need to ensure the output buffer is zeroed. */
19735 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
19736
19737 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
19738 totalPCMFramesProcessed += framesToProcess;
19739 }
19740
19741 /* 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. */
19742 if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
19743 pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
19744 }
19745
19746 /* If we've consumed the whole buffer we can return now. */
19747 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
19748 if (totalPCMFramesProcessed == frameCount) {
19749 break;
19750 }
19751
19752 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
19753 targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;
19754 for (;;) {
19755 ma_uint64 currentFrame;
19756
19757 /* Stop waiting if the device has been stopped. */
19758 if (!ma_device_is_started__null(pDevice)) {
19759 break;
19760 }
19761
19762 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
19763 if (currentFrame >= targetFrame) {
19764 break;
19765 }
19766
19767 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
19768 ma_sleep(10);
19769 }
19770
19771 pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames;
19772 pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;
19773 }
19774
19775 if (pFramesRead != NULL) {
19776 *pFramesRead = totalPCMFramesProcessed;
19777 }
19778
19779 return result;
19780 }
19781
19782 static ma_result ma_context_uninit__null(ma_context* pContext)
19783 {
19784 MA_ASSERT(pContext != NULL);
19785 MA_ASSERT(pContext->backend == ma_backend_null);
19786
19787 (void)pContext;
19788 return MA_SUCCESS;
19789 }
19790
19791 static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
19792 {
19793 MA_ASSERT(pContext != NULL);
19794
19795 (void)pConfig;
19796 (void)pContext;
19797
19798 pCallbacks->onContextInit = ma_context_init__null;
19799 pCallbacks->onContextUninit = ma_context_uninit__null;
19800 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;
19801 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null;
19802 pCallbacks->onDeviceInit = ma_device_init__null;
19803 pCallbacks->onDeviceUninit = ma_device_uninit__null;
19804 pCallbacks->onDeviceStart = ma_device_start__null;
19805 pCallbacks->onDeviceStop = ma_device_stop__null;
19806 pCallbacks->onDeviceRead = ma_device_read__null;
19807 pCallbacks->onDeviceWrite = ma_device_write__null;
19808 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. */
19809
19810 /* The null backend always works. */
19811 return MA_SUCCESS;
19812 }
19813 #endif
19814
19815
19816
19817 /*******************************************************************************
19818
19819 WIN32 COMMON
19820
19821 *******************************************************************************/
19822 #if defined(MA_WIN32)
19823 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
19824 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved))
19825 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
19826 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
19827 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
19828 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
19829 #else
19830 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
19831 #define ma_CoUninitialize(pContext) CoUninitialize()
19832 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
19833 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
19834 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
19835 #endif
19836
19837 #if !defined(MAXULONG_PTR) && !defined(__WATCOMC__)
19838 typedef size_t DWORD_PTR;
19839 #endif
19840
19841 #if !defined(WAVE_FORMAT_1M08)
19842 #define WAVE_FORMAT_1M08 0x00000001
19843 #define WAVE_FORMAT_1S08 0x00000002
19844 #define WAVE_FORMAT_1M16 0x00000004
19845 #define WAVE_FORMAT_1S16 0x00000008
19846 #define WAVE_FORMAT_2M08 0x00000010
19847 #define WAVE_FORMAT_2S08 0x00000020
19848 #define WAVE_FORMAT_2M16 0x00000040
19849 #define WAVE_FORMAT_2S16 0x00000080
19850 #define WAVE_FORMAT_4M08 0x00000100
19851 #define WAVE_FORMAT_4S08 0x00000200
19852 #define WAVE_FORMAT_4M16 0x00000400
19853 #define WAVE_FORMAT_4S16 0x00000800
19854 #endif
19855
19856 #if !defined(WAVE_FORMAT_44M08)
19857 #define WAVE_FORMAT_44M08 0x00000100
19858 #define WAVE_FORMAT_44S08 0x00000200
19859 #define WAVE_FORMAT_44M16 0x00000400
19860 #define WAVE_FORMAT_44S16 0x00000800
19861 #define WAVE_FORMAT_48M08 0x00001000
19862 #define WAVE_FORMAT_48S08 0x00002000
19863 #define WAVE_FORMAT_48M16 0x00004000
19864 #define WAVE_FORMAT_48S16 0x00008000
19865 #define WAVE_FORMAT_96M08 0x00010000
19866 #define WAVE_FORMAT_96S08 0x00020000
19867 #define WAVE_FORMAT_96M16 0x00040000
19868 #define WAVE_FORMAT_96S16 0x00080000
19869 #endif
19870
19871 #ifndef SPEAKER_FRONT_LEFT
19872 #define SPEAKER_FRONT_LEFT 0x1
19873 #define SPEAKER_FRONT_RIGHT 0x2
19874 #define SPEAKER_FRONT_CENTER 0x4
19875 #define SPEAKER_LOW_FREQUENCY 0x8
19876 #define SPEAKER_BACK_LEFT 0x10
19877 #define SPEAKER_BACK_RIGHT 0x20
19878 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
19879 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
19880 #define SPEAKER_BACK_CENTER 0x100
19881 #define SPEAKER_SIDE_LEFT 0x200
19882 #define SPEAKER_SIDE_RIGHT 0x400
19883 #define SPEAKER_TOP_CENTER 0x800
19884 #define SPEAKER_TOP_FRONT_LEFT 0x1000
19885 #define SPEAKER_TOP_FRONT_CENTER 0x2000
19886 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
19887 #define SPEAKER_TOP_BACK_LEFT 0x8000
19888 #define SPEAKER_TOP_BACK_CENTER 0x10000
19889 #define SPEAKER_TOP_BACK_RIGHT 0x20000
19890 #endif
19891
19892 /*
19893 Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this
19894 because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The
19895 standard version uses tight packing, but for compiler compatibility we're not doing that with ours.
19896 */
19897 typedef struct
19898 {
19899 WORD wFormatTag;
19900 WORD nChannels;
19901 DWORD nSamplesPerSec;
19902 DWORD nAvgBytesPerSec;
19903 WORD nBlockAlign;
19904 WORD wBitsPerSample;
19905 WORD cbSize;
19906 } MA_WAVEFORMATEX;
19907
19908 typedef struct
19909 {
19910 WORD wFormatTag;
19911 WORD nChannels;
19912 DWORD nSamplesPerSec;
19913 DWORD nAvgBytesPerSec;
19914 WORD nBlockAlign;
19915 WORD wBitsPerSample;
19916 WORD cbSize;
19917 union
19918 {
19919 WORD wValidBitsPerSample;
19920 WORD wSamplesPerBlock;
19921 WORD wReserved;
19922 } Samples;
19923 DWORD dwChannelMask;
19924 GUID SubFormat;
19925 } MA_WAVEFORMATEXTENSIBLE;
19926
19927
19928
19929 #ifndef WAVE_FORMAT_EXTENSIBLE
19930 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
19931 #endif
19932
19933 #ifndef WAVE_FORMAT_PCM
19934 #define WAVE_FORMAT_PCM 1
19935 #endif
19936
19937 #ifndef WAVE_FORMAT_IEEE_FLOAT
19938 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
19939 #endif
19940
19941 /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
19942 static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
19943 {
19944 switch (id)
19945 {
19946 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
19947 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
19948 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
19949 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
19950 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
19951 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
19952 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
19953 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
19954 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
19955 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
19956 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
19957 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
19958 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
19959 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
19960 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
19961 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
19962 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
19963 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
19964 default: return 0;
19965 }
19966 }
19967
19968 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
19969 static DWORD ma_channel_id_to_win32(DWORD id)
19970 {
19971 switch (id)
19972 {
19973 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
19974 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
19975 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
19976 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
19977 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
19978 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
19979 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
19980 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
19981 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
19982 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
19983 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
19984 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
19985 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
19986 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
19987 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
19988 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
19989 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
19990 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
19991 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
19992 default: return 0;
19993 }
19994 }
19995
19996 /* Converts a channel mapping to a Win32-style channel mask. */
19997 static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)
19998 {
19999 DWORD dwChannelMask = 0;
20000 ma_uint32 iChannel;
20001
20002 for (iChannel = 0; iChannel < channels; ++iChannel) {
20003 dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);
20004 }
20005
20006 return dwChannelMask;
20007 }
20008
20009 /* Converts a Win32-style channel mask to a miniaudio channel map. */
20010 static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)
20011 {
20012 /* If the channel mask is set to 0, just assume a default Win32 channel map. */
20013 if (dwChannelMask == 0) {
20014 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels);
20015 } else {
20016 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
20017 pChannelMap[0] = MA_CHANNEL_MONO;
20018 } else {
20019 /* Just iterate over each bit. */
20020 ma_uint32 iChannel = 0;
20021 ma_uint32 iBit;
20022
20023 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
20024 DWORD bitValue = (dwChannelMask & (1UL << iBit));
20025 if (bitValue != 0) {
20026 /* The bit is set. */
20027 pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
20028 iChannel += 1;
20029 }
20030 }
20031 }
20032 }
20033 }
20034
20035 #ifdef __cplusplus
20036 static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
20037 {
20038 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
20039 }
20040 #else
20041 #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
20042 #endif
20043
20044 static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)
20045 {
20046 static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
20047 return ma_is_guid_equal(guid, &nullguid);
20048 }
20049
20050 static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF)
20051 {
20052 MA_ASSERT(pWF != NULL);
20053
20054 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
20055 const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF;
20056 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
20057 if (pWFEX->Samples.wValidBitsPerSample == 32) {
20058 return ma_format_s32;
20059 }
20060 if (pWFEX->Samples.wValidBitsPerSample == 24) {
20061 if (pWFEX->wBitsPerSample == 32) {
20062 return ma_format_s32;
20063 }
20064 if (pWFEX->wBitsPerSample == 24) {
20065 return ma_format_s24;
20066 }
20067 }
20068 if (pWFEX->Samples.wValidBitsPerSample == 16) {
20069 return ma_format_s16;
20070 }
20071 if (pWFEX->Samples.wValidBitsPerSample == 8) {
20072 return ma_format_u8;
20073 }
20074 }
20075 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
20076 if (pWFEX->Samples.wValidBitsPerSample == 32) {
20077 return ma_format_f32;
20078 }
20079 /*
20080 if (pWFEX->Samples.wValidBitsPerSample == 64) {
20081 return ma_format_f64;
20082 }
20083 */
20084 }
20085 } else {
20086 if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
20087 if (pWF->wBitsPerSample == 32) {
20088 return ma_format_s32;
20089 }
20090 if (pWF->wBitsPerSample == 24) {
20091 return ma_format_s24;
20092 }
20093 if (pWF->wBitsPerSample == 16) {
20094 return ma_format_s16;
20095 }
20096 if (pWF->wBitsPerSample == 8) {
20097 return ma_format_u8;
20098 }
20099 }
20100 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
20101 if (pWF->wBitsPerSample == 32) {
20102 return ma_format_f32;
20103 }
20104 if (pWF->wBitsPerSample == 64) {
20105 /*return ma_format_f64;*/
20106 }
20107 }
20108 }
20109
20110 return ma_format_unknown;
20111 }
20112 #endif
20113
20114
20115 /*******************************************************************************
20116
20117 WASAPI Backend
20118
20119 *******************************************************************************/
20120 #ifdef MA_HAS_WASAPI
20121 #if 0
20122 #if defined(_MSC_VER)
20123 #pragma warning(push)
20124 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
20125 #endif
20126 #include <audioclient.h>
20127 #include <mmdeviceapi.h>
20128 #if defined(_MSC_VER)
20129 #pragma warning(pop)
20130 #endif
20131 #endif /* 0 */
20132
20133 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType);
20134
20135 /* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
20136 #define MA_WIN32_WINNT_VISTA 0x0600
20137 #define MA_VER_MINORVERSION 0x01
20138 #define MA_VER_MAJORVERSION 0x02
20139 #define MA_VER_SERVICEPACKMAJOR 0x20
20140 #define MA_VER_GREATER_EQUAL 0x03
20141
20142 typedef struct {
20143 DWORD dwOSVersionInfoSize;
20144 DWORD dwMajorVersion;
20145 DWORD dwMinorVersion;
20146 DWORD dwBuildNumber;
20147 DWORD dwPlatformId;
20148 WCHAR szCSDVersion[128];
20149 WORD wServicePackMajor;
20150 WORD wServicePackMinor;
20151 WORD wSuiteMask;
20152 BYTE wProductType;
20153 BYTE wReserved;
20154 } ma_OSVERSIONINFOEXW;
20155
20156 typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
20157 typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
20158
20159
20160 #ifndef PROPERTYKEY_DEFINED
20161 #define PROPERTYKEY_DEFINED
20162 #ifndef __WATCOMC__
20163 typedef struct
20164 {
20165 GUID fmtid;
20166 DWORD pid;
20167 } PROPERTYKEY;
20168 #endif
20169 #endif
20170
20171 /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
20172 static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp)
20173 {
20174 MA_ZERO_OBJECT(pProp);
20175 }
20176
20177
20178 static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
20179 static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
20180
20181 static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
20182 #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
20183 static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
20184 #endif
20185
20186 static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
20187 static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
20188 static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
20189 static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
20190 static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
20191 static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
20192 #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
20193 static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
20194 static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
20195 static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
20196 #endif
20197
20198 static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
20199 static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
20200
20201 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20202 #define MA_MM_DEVICE_STATE_ACTIVE 1
20203 #define MA_MM_DEVICE_STATE_DISABLED 2
20204 #define MA_MM_DEVICE_STATE_NOTPRESENT 4
20205 #define MA_MM_DEVICE_STATE_UNPLUGGED 8
20206
20207 typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
20208 typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
20209 typedef struct ma_IMMDevice ma_IMMDevice;
20210 #else
20211 typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
20212 typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
20213 #endif
20214 typedef struct ma_IPropertyStore ma_IPropertyStore;
20215 typedef struct ma_IAudioClient ma_IAudioClient;
20216 typedef struct ma_IAudioClient2 ma_IAudioClient2;
20217 typedef struct ma_IAudioClient3 ma_IAudioClient3;
20218 typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
20219 typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
20220
20221 typedef ma_int64 MA_REFERENCE_TIME;
20222
20223 #define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
20224 #define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
20225 #define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
20226 #define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
20227 #define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
20228 #define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
20229 #define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
20230 #define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
20231 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
20232 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
20233
20234 /* Buffer flags. */
20235 #define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
20236 #define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
20237 #define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
20238
20239 typedef enum
20240 {
20241 ma_eRender = 0,
20242 ma_eCapture = 1,
20243 ma_eAll = 2
20244 } ma_EDataFlow;
20245
20246 typedef enum
20247 {
20248 ma_eConsole = 0,
20249 ma_eMultimedia = 1,
20250 ma_eCommunications = 2
20251 } ma_ERole;
20252
20253 typedef enum
20254 {
20255 MA_AUDCLNT_SHAREMODE_SHARED,
20256 MA_AUDCLNT_SHAREMODE_EXCLUSIVE
20257 } MA_AUDCLNT_SHAREMODE;
20258
20259 typedef enum
20260 {
20261 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
20262 } MA_AUDIO_STREAM_CATEGORY;
20263
20264 typedef struct
20265 {
20266 ma_uint32 cbSize;
20267 BOOL bIsOffload;
20268 MA_AUDIO_STREAM_CATEGORY eCategory;
20269 } ma_AudioClientProperties;
20270
20271 /* IUnknown */
20272 typedef struct
20273 {
20274 /* IUnknown */
20275 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
20276 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
20277 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
20278 } ma_IUnknownVtbl;
20279 struct ma_IUnknown
20280 {
20281 ma_IUnknownVtbl* lpVtbl;
20282 };
20283 static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20284 static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20285 static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
20286
20287 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20288 /* IMMNotificationClient */
20289 typedef struct
20290 {
20291 /* IUnknown */
20292 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
20293 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
20294 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
20295
20296 /* IMMNotificationClient */
20297 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState);
20298 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);
20299 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);
20300 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID);
20301 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key);
20302 } ma_IMMNotificationClientVtbl;
20303
20304 /* IMMDeviceEnumerator */
20305 typedef struct
20306 {
20307 /* IUnknown */
20308 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
20309 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
20310 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
20311
20312 /* IMMDeviceEnumerator */
20313 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
20314 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
20315 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice);
20316 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
20317 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
20318 } ma_IMMDeviceEnumeratorVtbl;
20319 struct ma_IMMDeviceEnumerator
20320 {
20321 ma_IMMDeviceEnumeratorVtbl* lpVtbl;
20322 };
20323 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20324 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20325 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
20326 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); }
20327 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); }
20328 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
20329 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
20330 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
20331
20332
20333 /* IMMDeviceCollection */
20334 typedef struct
20335 {
20336 /* IUnknown */
20337 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
20338 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
20339 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
20340
20341 /* IMMDeviceCollection */
20342 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
20343 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
20344 } ma_IMMDeviceCollectionVtbl;
20345 struct ma_IMMDeviceCollection
20346 {
20347 ma_IMMDeviceCollectionVtbl* lpVtbl;
20348 };
20349 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20350 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20351 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
20352 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
20353 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
20354
20355
20356 /* IMMDevice */
20357 typedef struct
20358 {
20359 /* IUnknown */
20360 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
20361 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
20362 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
20363
20364 /* IMMDevice */
20365 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface);
20366 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
20367 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID);
20368 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
20369 } ma_IMMDeviceVtbl;
20370 struct ma_IMMDevice
20371 {
20372 ma_IMMDeviceVtbl* lpVtbl;
20373 };
20374 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20375 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20376 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
20377 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); }
20378 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
20379 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); }
20380 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
20381 #else
20382 /* IActivateAudioInterfaceAsyncOperation */
20383 typedef struct
20384 {
20385 /* IUnknown */
20386 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
20387 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
20388 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
20389
20390 /* IActivateAudioInterfaceAsyncOperation */
20391 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
20392 } ma_IActivateAudioInterfaceAsyncOperationVtbl;
20393 struct ma_IActivateAudioInterfaceAsyncOperation
20394 {
20395 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
20396 };
20397 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20398 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20399 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
20400 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
20401 #endif
20402
20403 /* IPropertyStore */
20404 typedef struct
20405 {
20406 /* IUnknown */
20407 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
20408 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
20409 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
20410
20411 /* IPropertyStore */
20412 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
20413 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
20414 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar);
20415 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar);
20416 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
20417 } ma_IPropertyStoreVtbl;
20418 struct ma_IPropertyStore
20419 {
20420 ma_IPropertyStoreVtbl* lpVtbl;
20421 };
20422 static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20423 static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20424 static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
20425 static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
20426 static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
20427 static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
20428 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); }
20429 static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
20430
20431
20432 /* IAudioClient */
20433 typedef struct
20434 {
20435 /* IUnknown */
20436 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
20437 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
20438 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
20439
20440 /* IAudioClient */
20441 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);
20442 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
20443 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
20444 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
20445 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
20446 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
20447 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
20448 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
20449 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
20450 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
20451 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
20452 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
20453 } ma_IAudioClientVtbl;
20454 struct ma_IAudioClient
20455 {
20456 ma_IAudioClientVtbl* lpVtbl;
20457 };
20458 static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20459 static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20460 static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
20461 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); }
20462 static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
20463 static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
20464 static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
20465 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); }
20466 static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
20467 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); }
20468 static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
20469 static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
20470 static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
20471 static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
20472 static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
20473
20474 /* IAudioClient2 */
20475 typedef struct
20476 {
20477 /* IUnknown */
20478 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
20479 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
20480 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
20481
20482 /* IAudioClient */
20483 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);
20484 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
20485 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
20486 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
20487 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
20488 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
20489 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
20490 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
20491 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
20492 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
20493 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
20494 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
20495
20496 /* IAudioClient2 */
20497 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
20498 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
20499 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
20500 } ma_IAudioClient2Vtbl;
20501 struct ma_IAudioClient2
20502 {
20503 ma_IAudioClient2Vtbl* lpVtbl;
20504 };
20505 static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20506 static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20507 static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
20508 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); }
20509 static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
20510 static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
20511 static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
20512 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); }
20513 static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
20514 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); }
20515 static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
20516 static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
20517 static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
20518 static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
20519 static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
20520 static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
20521 static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
20522 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); }
20523
20524
20525 /* IAudioClient3 */
20526 typedef struct
20527 {
20528 /* IUnknown */
20529 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
20530 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
20531 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
20532
20533 /* IAudioClient */
20534 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);
20535 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
20536 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
20537 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
20538 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
20539 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
20540 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
20541 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
20542 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
20543 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
20544 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
20545 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
20546
20547 /* IAudioClient2 */
20548 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
20549 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
20550 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
20551
20552 /* IAudioClient3 */
20553 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames);
20554 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames);
20555 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
20556 } ma_IAudioClient3Vtbl;
20557 struct ma_IAudioClient3
20558 {
20559 ma_IAudioClient3Vtbl* lpVtbl;
20560 };
20561 static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20562 static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20563 static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
20564 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); }
20565 static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
20566 static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
20567 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
20568 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); }
20569 static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
20570 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); }
20571 static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
20572 static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
20573 static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
20574 static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
20575 static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
20576 static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
20577 static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
20578 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); }
20579 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); }
20580 static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
20581 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); }
20582
20583
20584 /* IAudioRenderClient */
20585 typedef struct
20586 {
20587 /* IUnknown */
20588 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
20589 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
20590 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
20591
20592 /* IAudioRenderClient */
20593 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
20594 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
20595 } ma_IAudioRenderClientVtbl;
20596 struct ma_IAudioRenderClient
20597 {
20598 ma_IAudioRenderClientVtbl* lpVtbl;
20599 };
20600 static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20601 static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20602 static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
20603 static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
20604 static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
20605
20606
20607 /* IAudioCaptureClient */
20608 typedef struct
20609 {
20610 /* IUnknown */
20611 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
20612 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
20613 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
20614
20615 /* IAudioRenderClient */
20616 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
20617 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
20618 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
20619 } ma_IAudioCaptureClientVtbl;
20620 struct ma_IAudioCaptureClient
20621 {
20622 ma_IAudioCaptureClientVtbl* lpVtbl;
20623 };
20624 static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
20625 static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
20626 static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
20627 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); }
20628 static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
20629 static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
20630
20631 #if defined(MA_WIN32_UWP)
20632 /* mmdevapi Functions */
20633 typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation);
20634 #endif
20635
20636 /* Avrt Functions */
20637 typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex);
20638 typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle);
20639
20640 #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
20641 typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
20642
20643 typedef struct
20644 {
20645 /* IUnknown */
20646 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
20647 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
20648 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
20649
20650 /* IActivateAudioInterfaceCompletionHandler */
20651 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
20652 } ma_completion_handler_uwp_vtbl;
20653 struct ma_completion_handler_uwp
20654 {
20655 ma_completion_handler_uwp_vtbl* lpVtbl;
20656 MA_ATOMIC(4, ma_uint32) counter;
20657 HANDLE hEvent;
20658 };
20659
20660 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
20661 {
20662 /*
20663 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
20664 "implement" this, we just make sure we return pThis when the IAgileObject is requested.
20665 */
20666 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)) {
20667 *ppObject = NULL;
20668 return E_NOINTERFACE;
20669 }
20670
20671 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
20672 *ppObject = (void*)pThis;
20673 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
20674 return S_OK;
20675 }
20676
20677 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
20678 {
20679 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
20680 }
20681
20682 static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
20683 {
20684 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
20685 if (newRefCount == 0) {
20686 return 0; /* We don't free anything here because we never allocate the object on the heap. */
20687 }
20688
20689 return (ULONG)newRefCount;
20690 }
20691
20692 static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
20693 {
20694 (void)pActivateOperation;
20695 SetEvent(pThis->hEvent);
20696 return S_OK;
20697 }
20698
20699
20700 static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
20701 ma_completion_handler_uwp_QueryInterface,
20702 ma_completion_handler_uwp_AddRef,
20703 ma_completion_handler_uwp_Release,
20704 ma_completion_handler_uwp_ActivateCompleted
20705 };
20706
20707 static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
20708 {
20709 MA_ASSERT(pHandler != NULL);
20710 MA_ZERO_OBJECT(pHandler);
20711
20712 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
20713 pHandler->counter = 1;
20714 pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
20715 if (pHandler->hEvent == NULL) {
20716 return ma_result_from_GetLastError(GetLastError());
20717 }
20718
20719 return MA_SUCCESS;
20720 }
20721
20722 static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
20723 {
20724 if (pHandler->hEvent != NULL) {
20725 CloseHandle(pHandler->hEvent);
20726 }
20727 }
20728
20729 static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
20730 {
20731 WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE);
20732 }
20733 #endif /* !MA_WIN32_DESKTOP */
20734
20735 /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
20736 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
20737 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
20738 {
20739 /*
20740 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
20741 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
20742 */
20743 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
20744 *ppObject = NULL;
20745 return E_NOINTERFACE;
20746 }
20747
20748 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
20749 *ppObject = (void*)pThis;
20750 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
20751 return S_OK;
20752 }
20753
20754 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
20755 {
20756 return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
20757 }
20758
20759 static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
20760 {
20761 ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
20762 if (newRefCount == 0) {
20763 return 0; /* We don't free anything here because we never allocate the object on the heap. */
20764 }
20765
20766 return (ULONG)newRefCount;
20767 }
20768
20769 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState)
20770 {
20771 ma_bool32 isThisDevice = MA_FALSE;
20772 ma_bool32 isCapture = MA_FALSE;
20773 ma_bool32 isPlayback = MA_FALSE;
20774
20775 #ifdef MA_DEBUG_OUTPUT
20776 /*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);*/
20777 #endif
20778
20779 /*
20780 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
20781 that the device is disabled or has been unplugged.
20782 */
20783 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)) {
20784 isCapture = MA_TRUE;
20785 if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
20786 isThisDevice = MA_TRUE;
20787 }
20788 }
20789
20790 if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
20791 isPlayback = MA_TRUE;
20792 if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
20793 isThisDevice = MA_TRUE;
20794 }
20795 }
20796
20797
20798 /*
20799 If the device ID matches our device we need to mark our device as detached and stop it. When a
20800 device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device
20801 was started at the time of being removed.
20802 */
20803 if (isThisDevice) {
20804 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {
20805 /*
20806 Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll
20807 use this to determine whether or not we need to automatically start the device when it's
20808 plugged back in again.
20809 */
20810 if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) {
20811 if (isPlayback) {
20812 pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;
20813 }
20814 if (isCapture) {
20815 pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;
20816 }
20817
20818 ma_device_stop(pThis->pDevice);
20819 }
20820 }
20821
20822 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
20823 /* The device was activated. If we were detached, we need to start it again. */
20824 ma_bool8 tryRestartingDevice = MA_FALSE;
20825
20826 if (isPlayback) {
20827 if (pThis->pDevice->wasapi.isDetachedPlayback) {
20828 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
20829 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
20830 tryRestartingDevice = MA_TRUE;
20831 }
20832 }
20833
20834 if (isCapture) {
20835 if (pThis->pDevice->wasapi.isDetachedCapture) {
20836 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
20837 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
20838 tryRestartingDevice = MA_TRUE;
20839 }
20840 }
20841
20842 if (tryRestartingDevice) {
20843 if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {
20844 ma_device_start(pThis->pDevice);
20845 }
20846 }
20847 }
20848 }
20849
20850 return S_OK;
20851 }
20852
20853 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)
20854 {
20855 #ifdef MA_DEBUG_OUTPUT
20856 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
20857 #endif
20858
20859 /* We don't need to worry about this event for our purposes. */
20860 (void)pThis;
20861 (void)pDeviceID;
20862 return S_OK;
20863 }
20864
20865 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)
20866 {
20867 #ifdef MA_DEBUG_OUTPUT
20868 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
20869 #endif
20870
20871 /* We don't need to worry about this event for our purposes. */
20872 (void)pThis;
20873 (void)pDeviceID;
20874 return S_OK;
20875 }
20876
20877 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID)
20878 {
20879 #ifdef MA_DEBUG_OUTPUT
20880 /*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)");*/
20881 #endif
20882
20883 (void)role;
20884
20885 /* We only care about devices with the same data flow as the current device. */
20886 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
20887 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) ||
20888 (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) {
20889 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n");
20890 return S_OK;
20891 }
20892
20893 /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */
20894 if (pThis->pDevice->type == ma_device_type_loopback) {
20895 dataFlow = ma_eCapture;
20896 }
20897
20898 /* Don't do automatic stream routing if we're not allowed. */
20899 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
20900 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
20901 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");
20902 return S_OK;
20903 }
20904
20905 /*
20906 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
20907 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
20908 it's fixed.
20909 */
20910 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
20911 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
20912 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");
20913 return S_OK;
20914 }
20915
20916
20917
20918 /*
20919 Second attempt at device rerouting. We're going to retrieve the device's state at the time of
20920 the route change. We're then going to stop the device, reinitialize the device, and then start
20921 it again if the state before stopping was ma_device_state_started.
20922 */
20923 {
20924 ma_uint32 previousState = ma_device_get_state(pThis->pDevice);
20925 ma_bool8 restartDevice = MA_FALSE;
20926
20927 if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) {
20928 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");
20929 return S_OK;
20930 }
20931
20932 if (previousState == ma_device_state_started) {
20933 ma_device_stop(pThis->pDevice);
20934 restartDevice = MA_TRUE;
20935 }
20936
20937 if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */
20938 ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock);
20939 {
20940 if (dataFlow == ma_eRender) {
20941 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
20942
20943 if (pThis->pDevice->wasapi.isDetachedPlayback) {
20944 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
20945
20946 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
20947 restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
20948 }
20949 else {
20950 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. */
20951 }
20952 }
20953 }
20954 else {
20955 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
20956
20957 if (pThis->pDevice->wasapi.isDetachedCapture) {
20958 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
20959
20960 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
20961 restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
20962 }
20963 else {
20964 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. */
20965 }
20966 }
20967 }
20968 }
20969 ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock);
20970
20971 if (restartDevice) {
20972 ma_device_start(pThis->pDevice);
20973 }
20974 }
20975 }
20976
20977 return S_OK;
20978 }
20979
20980 static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key)
20981 {
20982 #ifdef MA_DEBUG_OUTPUT
20983 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
20984 #endif
20985
20986 (void)pThis;
20987 (void)pDeviceID;
20988 (void)key;
20989 return S_OK;
20990 }
20991
20992 static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
20993 ma_IMMNotificationClient_QueryInterface,
20994 ma_IMMNotificationClient_AddRef,
20995 ma_IMMNotificationClient_Release,
20996 ma_IMMNotificationClient_OnDeviceStateChanged,
20997 ma_IMMNotificationClient_OnDeviceAdded,
20998 ma_IMMNotificationClient_OnDeviceRemoved,
20999 ma_IMMNotificationClient_OnDefaultDeviceChanged,
21000 ma_IMMNotificationClient_OnPropertyValueChanged
21001 };
21002 #endif /* MA_WIN32_DESKTOP */
21003
21004 static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage)
21005 {
21006 switch (usage)
21007 {
21008 case ma_wasapi_usage_default: return NULL;
21009 case ma_wasapi_usage_games: return "Games";
21010 case ma_wasapi_usage_pro_audio: return "Pro Audio";
21011 default: break;
21012 }
21013
21014 return NULL;
21015 }
21016
21017 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21018 typedef ma_IMMDevice ma_WASAPIDeviceInterface;
21019 #else
21020 typedef ma_IUnknown ma_WASAPIDeviceInterface;
21021 #endif
21022
21023
21024 #define MA_CONTEXT_COMMAND_QUIT__WASAPI 1
21025 #define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2
21026 #define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3
21027
21028 static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
21029 {
21030 ma_context_command__wasapi cmd;
21031
21032 MA_ZERO_OBJECT(&cmd);
21033 cmd.code = code;
21034
21035 return cmd;
21036 }
21037
21038 static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)
21039 {
21040 /* For now we are doing everything synchronously, but I might relax this later if the need arises. */
21041 ma_result result;
21042 ma_bool32 isUsingLocalEvent = MA_FALSE;
21043 ma_event localEvent;
21044
21045 MA_ASSERT(pContext != NULL);
21046 MA_ASSERT(pCmd != NULL);
21047
21048 if (pCmd->pEvent == NULL) {
21049 isUsingLocalEvent = MA_TRUE;
21050
21051 result = ma_event_init(&localEvent);
21052 if (result != MA_SUCCESS) {
21053 return result; /* Failed to create the event for this command. */
21054 }
21055 }
21056
21057 /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */
21058 ma_mutex_lock(&pContext->wasapi.commandLock);
21059 {
21060 ma_uint32 index;
21061
21062 /* Spin until we've got some space available. */
21063 while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {
21064 ma_yield();
21065 }
21066
21067 /* Space is now available. Can safely add to the list. */
21068 index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);
21069 pContext->wasapi.commands[index] = *pCmd;
21070 pContext->wasapi.commands[index].pEvent = &localEvent;
21071 pContext->wasapi.commandCount += 1;
21072
21073 /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */
21074 ma_semaphore_release(&pContext->wasapi.commandSem);
21075 }
21076 ma_mutex_unlock(&pContext->wasapi.commandLock);
21077
21078 if (isUsingLocalEvent) {
21079 ma_event_wait(&localEvent);
21080 ma_event_uninit(&localEvent);
21081 }
21082
21083 return MA_SUCCESS;
21084 }
21085
21086 static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
21087 {
21088 ma_result result = MA_SUCCESS;
21089
21090 MA_ASSERT(pContext != NULL);
21091 MA_ASSERT(pCmd != NULL);
21092
21093 result = ma_semaphore_wait(&pContext->wasapi.commandSem);
21094 if (result == MA_SUCCESS) {
21095 ma_mutex_lock(&pContext->wasapi.commandLock);
21096 {
21097 *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];
21098 pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);
21099 pContext->wasapi.commandCount -= 1;
21100 }
21101 ma_mutex_unlock(&pContext->wasapi.commandLock);
21102 }
21103
21104 return result;
21105 }
21106
21107 static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
21108 {
21109 ma_result result;
21110 ma_context* pContext = (ma_context*)pUserData;
21111 MA_ASSERT(pContext != NULL);
21112
21113 for (;;) {
21114 ma_context_command__wasapi cmd;
21115 result = ma_context_next_command__wasapi(pContext, &cmd);
21116 if (result != MA_SUCCESS) {
21117 break;
21118 }
21119
21120 switch (cmd.code)
21121 {
21122 case MA_CONTEXT_COMMAND_QUIT__WASAPI:
21123 {
21124 /* Do nothing. Handled after the switch. */
21125 } break;
21126
21127 case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
21128 {
21129 if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) {
21130 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
21131 } else {
21132 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
21133 }
21134 } break;
21135
21136 case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
21137 {
21138 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {
21139 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
21140 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
21141 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
21142 }
21143 }
21144
21145 if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) {
21146 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {
21147 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);
21148 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;
21149 }
21150 }
21151 } break;
21152
21153 default:
21154 {
21155 /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */
21156 MA_ASSERT(MA_FALSE);
21157 } break;
21158 }
21159
21160 if (cmd.pEvent != NULL) {
21161 ma_event_signal(cmd.pEvent);
21162 }
21163
21164 if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {
21165 break; /* Received a quit message. Get out of here. */
21166 }
21167 }
21168
21169 return (ma_thread_result)0;
21170 }
21171
21172 static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
21173 {
21174 ma_result result;
21175 ma_result cmdResult;
21176 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
21177 cmd.data.createAudioClient.deviceType = deviceType;
21178 cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
21179 cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
21180 cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */
21181
21182 result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */
21183 if (result != MA_SUCCESS) {
21184 return result;
21185 }
21186
21187 return *cmd.data.createAudioClient.pResult;
21188 }
21189
21190 #if 0 /* Not used at the moment, but leaving here for future use. */
21191 static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)
21192 {
21193 ma_result result;
21194 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);
21195 cmd.data.releaseAudioClient.pDevice = pDevice;
21196 cmd.data.releaseAudioClient.deviceType = deviceType;
21197
21198 result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */
21199 if (result != MA_SUCCESS) {
21200 return result;
21201 }
21202
21203 return MA_SUCCESS;
21204 }
21205 #endif
21206
21207
21208 static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)
21209 {
21210 MA_ASSERT(pWF != NULL);
21211 MA_ASSERT(pInfo != NULL);
21212
21213 if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {
21214 return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */
21215 }
21216
21217 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF);
21218 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels;
21219 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;
21220 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0;
21221 pInfo->nativeDataFormatCount += 1;
21222 }
21223
21224 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)
21225 {
21226 HRESULT hr;
21227 MA_WAVEFORMATEX* pWF = NULL;
21228
21229 MA_ASSERT(pAudioClient != NULL);
21230 MA_ASSERT(pInfo != NULL);
21231
21232 /* Shared Mode. We use GetMixFormat() here. */
21233 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF);
21234 if (SUCCEEDED(hr)) {
21235 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);
21236 } else {
21237 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.");
21238 return ma_result_from_HRESULT(hr);
21239 }
21240
21241 /*
21242 Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on
21243 UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on
21244 out, MA_SUCCESS is guaranteed to be returned.
21245 */
21246 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21247 {
21248 ma_IPropertyStore *pProperties;
21249
21250 /*
21251 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
21252 correct which will simplify our searching.
21253 */
21254 hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
21255 if (SUCCEEDED(hr)) {
21256 MA_PROPVARIANT var;
21257 ma_PropVariantInit(&var);
21258
21259 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
21260 if (SUCCEEDED(hr)) {
21261 pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData;
21262
21263 /*
21264 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
21265 first. If this fails, fall back to a search.
21266 */
21267 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
21268 if (SUCCEEDED(hr)) {
21269 /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */
21270 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);
21271 } else {
21272 /*
21273 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
21274 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
21275 */
21276 ma_uint32 channels = pWF->nChannels;
21277 ma_channel defaultChannelMap[MA_MAX_CHANNELS];
21278 MA_WAVEFORMATEXTENSIBLE wf;
21279 ma_bool32 found;
21280 ma_uint32 iFormat;
21281
21282 /* Make sure we don't overflow the channel map. */
21283 if (channels > MA_MAX_CHANNELS) {
21284 channels = MA_MAX_CHANNELS;
21285 }
21286
21287 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels);
21288
21289 MA_ZERO_OBJECT(&wf);
21290 wf.cbSize = sizeof(wf);
21291 wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
21292 wf.nChannels = (WORD)channels;
21293 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
21294
21295 found = MA_FALSE;
21296 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
21297 ma_format format = g_maFormatPriorities[iFormat];
21298 ma_uint32 iSampleRate;
21299
21300 wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
21301 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
21302 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
21303 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample;
21304 if (format == ma_format_f32) {
21305 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
21306 } else {
21307 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
21308 }
21309
21310 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
21311 wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
21312
21313 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL);
21314 if (SUCCEEDED(hr)) {
21315 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);
21316 found = MA_TRUE;
21317 break;
21318 }
21319 }
21320
21321 if (found) {
21322 break;
21323 }
21324 }
21325
21326 ma_PropVariantClear(pContext, &var);
21327
21328 if (!found) {
21329 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval.");
21330 }
21331 }
21332 } else {
21333 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval.");
21334 }
21335
21336 ma_IPropertyStore_Release(pProperties);
21337 } else {
21338 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval.");
21339 }
21340 }
21341 #else
21342 {
21343 (void)pMMDevice; /* Unused. */
21344 }
21345 #endif
21346
21347 return MA_SUCCESS;
21348 }
21349
21350 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21351 static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
21352 {
21353 if (deviceType == ma_device_type_playback) {
21354 return ma_eRender;
21355 } else if (deviceType == ma_device_type_capture) {
21356 return ma_eCapture;
21357 } else {
21358 MA_ASSERT(MA_FALSE);
21359 return ma_eRender; /* Should never hit this. */
21360 }
21361 }
21362
21363 static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
21364 {
21365 HRESULT hr;
21366 ma_IMMDeviceEnumerator* pDeviceEnumerator;
21367
21368 MA_ASSERT(pContext != NULL);
21369 MA_ASSERT(ppDeviceEnumerator != NULL);
21370
21371 *ppDeviceEnumerator = NULL; /* Safety. */
21372
21373 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
21374 if (FAILED(hr)) {
21375 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
21376 return ma_result_from_HRESULT(hr);
21377 }
21378
21379 *ppDeviceEnumerator = pDeviceEnumerator;
21380
21381 return MA_SUCCESS;
21382 }
21383
21384 static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
21385 {
21386 HRESULT hr;
21387 ma_IMMDevice* pMMDefaultDevice = NULL;
21388 WCHAR* pDefaultDeviceID = NULL;
21389 ma_EDataFlow dataFlow;
21390 ma_ERole role;
21391
21392 MA_ASSERT(pContext != NULL);
21393 MA_ASSERT(pDeviceEnumerator != NULL);
21394
21395 (void)pContext;
21396
21397 /* Grab the EDataFlow type from the device type. */
21398 dataFlow = ma_device_type_to_EDataFlow(deviceType);
21399
21400 /* The role is always eConsole, but we may make this configurable later. */
21401 role = ma_eConsole;
21402
21403 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
21404 if (FAILED(hr)) {
21405 return NULL;
21406 }
21407
21408 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
21409
21410 ma_IMMDevice_Release(pMMDefaultDevice);
21411 pMMDefaultDevice = NULL;
21412
21413 if (FAILED(hr)) {
21414 return NULL;
21415 }
21416
21417 return pDefaultDeviceID;
21418 }
21419
21420 static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
21421 {
21422 ma_result result;
21423 ma_IMMDeviceEnumerator* pDeviceEnumerator;
21424 WCHAR* pDefaultDeviceID = NULL;
21425
21426 MA_ASSERT(pContext != NULL);
21427
21428 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
21429 if (result != MA_SUCCESS) {
21430 return NULL;
21431 }
21432
21433 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
21434
21435 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
21436 return pDefaultDeviceID;
21437 }
21438
21439 static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
21440 {
21441 ma_IMMDeviceEnumerator* pDeviceEnumerator;
21442 HRESULT hr;
21443
21444 MA_ASSERT(pContext != NULL);
21445 MA_ASSERT(ppMMDevice != NULL);
21446
21447 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
21448 if (FAILED(hr)) {
21449 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n");
21450 return ma_result_from_HRESULT(hr);
21451 }
21452
21453 if (pDeviceID == NULL) {
21454 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
21455 } else {
21456 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
21457 }
21458
21459 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
21460 if (FAILED(hr)) {
21461 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n");
21462 return ma_result_from_HRESULT(hr);
21463 }
21464
21465 return MA_SUCCESS;
21466 }
21467
21468 static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)
21469 {
21470 WCHAR* pDeviceIDString;
21471 HRESULT hr;
21472
21473 MA_ASSERT(pDeviceID != NULL);
21474
21475 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);
21476 if (SUCCEEDED(hr)) {
21477 size_t idlen = ma_strlen_WCHAR(pDeviceIDString);
21478 if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
21479 ma_CoTaskMemFree(pContext, pDeviceIDString);
21480 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. */
21481 return MA_ERROR;
21482 }
21483
21484 MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));
21485 pDeviceID->wasapi[idlen] = '\0';
21486
21487 ma_CoTaskMemFree(pContext, pDeviceIDString);
21488
21489 return MA_SUCCESS;
21490 }
21491
21492 return MA_ERROR;
21493 }
21494
21495 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)
21496 {
21497 ma_result result;
21498 HRESULT hr;
21499
21500 MA_ASSERT(pContext != NULL);
21501 MA_ASSERT(pMMDevice != NULL);
21502 MA_ASSERT(pInfo != NULL);
21503
21504 /* ID. */
21505 result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
21506 if (result == MA_SUCCESS) {
21507 if (pDefaultDeviceID != NULL) {
21508 if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) {
21509 pInfo->isDefault = MA_TRUE;
21510 }
21511 }
21512 }
21513
21514 /* Description / Friendly Name */
21515 {
21516 ma_IPropertyStore *pProperties;
21517 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
21518 if (SUCCEEDED(hr)) {
21519 MA_PROPVARIANT var;
21520
21521 ma_PropVariantInit(&var);
21522 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
21523 if (SUCCEEDED(hr)) {
21524 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
21525 ma_PropVariantClear(pContext, &var);
21526 }
21527
21528 ma_IPropertyStore_Release(pProperties);
21529 }
21530 }
21531
21532 /* Format */
21533 if (!onlySimpleInfo) {
21534 ma_IAudioClient* pAudioClient;
21535 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
21536 if (SUCCEEDED(hr)) {
21537 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);
21538
21539 ma_IAudioClient_Release(pAudioClient);
21540 return result;
21541 } else {
21542 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.");
21543 return ma_result_from_HRESULT(hr);
21544 }
21545 }
21546
21547 return MA_SUCCESS;
21548 }
21549
21550 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)
21551 {
21552 ma_result result = MA_SUCCESS;
21553 UINT deviceCount;
21554 HRESULT hr;
21555 ma_uint32 iDevice;
21556 WCHAR* pDefaultDeviceID = NULL;
21557 ma_IMMDeviceCollection* pDeviceCollection = NULL;
21558
21559 MA_ASSERT(pContext != NULL);
21560 MA_ASSERT(callback != NULL);
21561
21562 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
21563 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
21564
21565 /* We need to enumerate the devices which returns a device collection. */
21566 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
21567 if (SUCCEEDED(hr)) {
21568 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
21569 if (FAILED(hr)) {
21570 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n");
21571 result = ma_result_from_HRESULT(hr);
21572 goto done;
21573 }
21574
21575 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
21576 ma_device_info deviceInfo;
21577 ma_IMMDevice* pMMDevice;
21578
21579 MA_ZERO_OBJECT(&deviceInfo);
21580
21581 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
21582 if (SUCCEEDED(hr)) {
21583 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
21584
21585 ma_IMMDevice_Release(pMMDevice);
21586 if (result == MA_SUCCESS) {
21587 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
21588 if (cbResult == MA_FALSE) {
21589 break;
21590 }
21591 }
21592 }
21593 }
21594 }
21595
21596 done:
21597 if (pDefaultDeviceID != NULL) {
21598 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
21599 pDefaultDeviceID = NULL;
21600 }
21601
21602 if (pDeviceCollection != NULL) {
21603 ma_IMMDeviceCollection_Release(pDeviceCollection);
21604 pDeviceCollection = NULL;
21605 }
21606
21607 return result;
21608 }
21609
21610 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)
21611 {
21612 ma_result result;
21613 HRESULT hr;
21614
21615 MA_ASSERT(pContext != NULL);
21616 MA_ASSERT(ppAudioClient != NULL);
21617 MA_ASSERT(ppMMDevice != NULL);
21618
21619 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
21620 if (result != MA_SUCCESS) {
21621 return result;
21622 }
21623
21624 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient);
21625 if (FAILED(hr)) {
21626 return ma_result_from_HRESULT(hr);
21627 }
21628
21629 return MA_SUCCESS;
21630 }
21631 #else
21632 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)
21633 {
21634 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
21635 ma_completion_handler_uwp completionHandler;
21636 IID iid;
21637 WCHAR* iidStr;
21638 HRESULT hr;
21639 ma_result result;
21640 HRESULT activateResult;
21641 ma_IUnknown* pActivatedInterface;
21642
21643 MA_ASSERT(pContext != NULL);
21644 MA_ASSERT(ppAudioClient != NULL);
21645
21646 if (pDeviceID != NULL) {
21647 iidStr = (WCHAR*)pDeviceID->wasapi;
21648 } else {
21649 if (deviceType == ma_device_type_capture) {
21650 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
21651 } else {
21652 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
21653 }
21654
21655 #if defined(__cplusplus)
21656 hr = StringFromIID(iid, &iidStr);
21657 #else
21658 hr = StringFromIID(&iid, &iidStr);
21659 #endif
21660 if (FAILED(hr)) {
21661 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");
21662 return ma_result_from_HRESULT(hr);
21663 }
21664 }
21665
21666 result = ma_completion_handler_uwp_init(&completionHandler);
21667 if (result != MA_SUCCESS) {
21668 ma_CoTaskMemFree(pContext, iidStr);
21669 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n");
21670 return result;
21671 }
21672
21673 hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
21674 if (FAILED(hr)) {
21675 ma_completion_handler_uwp_uninit(&completionHandler);
21676 ma_CoTaskMemFree(pContext, iidStr);
21677 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n");
21678 return ma_result_from_HRESULT(hr);
21679 }
21680
21681 if (pDeviceID == NULL) {
21682 ma_CoTaskMemFree(pContext, iidStr);
21683 }
21684
21685 /* Wait for the async operation for finish. */
21686 ma_completion_handler_uwp_wait(&completionHandler);
21687 ma_completion_handler_uwp_uninit(&completionHandler);
21688
21689 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
21690 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
21691
21692 if (FAILED(hr) || FAILED(activateResult)) {
21693 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n");
21694 return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult);
21695 }
21696
21697 /* Here is where we grab the IAudioClient interface. */
21698 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
21699 if (FAILED(hr)) {
21700 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n");
21701 return ma_result_from_HRESULT(hr);
21702 }
21703
21704 if (ppActivatedInterface) {
21705 *ppActivatedInterface = pActivatedInterface;
21706 } else {
21707 ma_IUnknown_Release(pActivatedInterface);
21708 }
21709
21710 return MA_SUCCESS;
21711 }
21712 #endif
21713
21714
21715 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */
21716 typedef enum
21717 {
21718 MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT,
21719 MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK
21720 } MA_AUDIOCLIENT_ACTIVATION_TYPE;
21721
21722 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */
21723 typedef enum
21724 {
21725 MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE,
21726 MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE
21727 } MA_PROCESS_LOOPBACK_MODE;
21728
21729 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */
21730 typedef struct
21731 {
21732 DWORD TargetProcessId;
21733 MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode;
21734 } MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS;
21735
21736 #if defined(_MSC_VER) && !defined(__clang__)
21737 #pragma warning(push)
21738 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
21739 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
21740 #pragma GCC diagnostic push
21741 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
21742 #if defined(__clang__)
21743 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
21744 #endif
21745 #endif
21746 /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */
21747 typedef struct
21748 {
21749 MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType;
21750 union
21751 {
21752 MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams;
21753 };
21754 } MA_AUDIOCLIENT_ACTIVATION_PARAMS;
21755 #if defined(_MSC_VER) && !defined(__clang__)
21756 #pragma warning(pop)
21757 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
21758 #pragma GCC diagnostic pop
21759 #endif
21760
21761 #define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback"
21762
21763 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)
21764 {
21765 ma_result result;
21766 ma_bool32 usingProcessLoopback = MA_FALSE;
21767 MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams;
21768 MA_PROPVARIANT activationParams;
21769 MA_PROPVARIANT* pActivationParams = NULL;
21770 ma_device_id virtualDeviceID;
21771
21772 /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */
21773 if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) {
21774 usingProcessLoopback = MA_TRUE;
21775 }
21776
21777 if (usingProcessLoopback) {
21778 MA_ZERO_OBJECT(&audioclientActivationParams);
21779 audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK;
21780 audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE;
21781 audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID;
21782
21783 ma_PropVariantInit(&activationParams);
21784 activationParams.vt = MA_VT_BLOB;
21785 activationParams.blob.cbSize = sizeof(audioclientActivationParams);
21786 activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams;
21787 pActivationParams = &activationParams;
21788
21789 /* When requesting a specific device ID we need to use a special device ID. */
21790 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. */
21791 pDeviceID = &virtualDeviceID;
21792 } else {
21793 pActivationParams = NULL; /* No activation parameters required. */
21794 }
21795
21796 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21797 result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
21798 #else
21799 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
21800 #endif
21801
21802 /*
21803 If loopback mode was requested with a process ID and initialization failed, it could be because it's
21804 trying to run on an older version of Windows where it's not supported. We need to let the caller
21805 know about this with a log message.
21806 */
21807 if (result != MA_SUCCESS) {
21808 if (usingProcessLoopback) {
21809 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);
21810 }
21811 }
21812
21813 return result;
21814 }
21815
21816
21817 static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
21818 {
21819 /* Different enumeration for desktop and UWP. */
21820 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21821 /* Desktop */
21822 HRESULT hr;
21823 ma_IMMDeviceEnumerator* pDeviceEnumerator;
21824
21825 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
21826 if (FAILED(hr)) {
21827 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
21828 return ma_result_from_HRESULT(hr);
21829 }
21830
21831 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
21832 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
21833
21834 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
21835 #else
21836 /*
21837 UWP
21838
21839 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
21840 over devices without using MMDevice, I'm restricting devices to defaults.
21841
21842 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/
21843 */
21844 if (callback) {
21845 ma_bool32 cbResult = MA_TRUE;
21846
21847 /* Playback. */
21848 if (cbResult) {
21849 ma_device_info deviceInfo;
21850 MA_ZERO_OBJECT(&deviceInfo);
21851 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
21852 deviceInfo.isDefault = MA_TRUE;
21853 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
21854 }
21855
21856 /* Capture. */
21857 if (cbResult) {
21858 ma_device_info deviceInfo;
21859 MA_ZERO_OBJECT(&deviceInfo);
21860 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
21861 deviceInfo.isDefault = MA_TRUE;
21862 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
21863 }
21864 }
21865 #endif
21866
21867 return MA_SUCCESS;
21868 }
21869
21870 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)
21871 {
21872 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21873 ma_result result;
21874 ma_IMMDevice* pMMDevice = NULL;
21875 WCHAR* pDefaultDeviceID = NULL;
21876
21877 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
21878 if (result != MA_SUCCESS) {
21879 return result;
21880 }
21881
21882 /* We need the default device ID so we can set the isDefault flag in the device info. */
21883 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
21884
21885 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
21886
21887 if (pDefaultDeviceID != NULL) {
21888 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
21889 pDefaultDeviceID = NULL;
21890 }
21891
21892 ma_IMMDevice_Release(pMMDevice);
21893
21894 return result;
21895 #else
21896 ma_IAudioClient* pAudioClient;
21897 ma_result result;
21898
21899 /* UWP currently only uses default devices. */
21900 if (deviceType == ma_device_type_playback) {
21901 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
21902 } else {
21903 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
21904 }
21905
21906 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL);
21907 if (result != MA_SUCCESS) {
21908 return result;
21909 }
21910
21911 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
21912
21913 pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */
21914
21915 ma_IAudioClient_Release(pAudioClient);
21916 return result;
21917 #endif
21918 }
21919
21920 static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
21921 {
21922 MA_ASSERT(pDevice != NULL);
21923
21924 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21925 if (pDevice->wasapi.pDeviceEnumerator) {
21926 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
21927 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
21928 }
21929 #endif
21930
21931 if (pDevice->wasapi.pRenderClient) {
21932 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
21933 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
21934 pDevice->wasapi.pMappedBufferPlayback = NULL;
21935 pDevice->wasapi.mappedBufferPlaybackCap = 0;
21936 pDevice->wasapi.mappedBufferPlaybackLen = 0;
21937 }
21938
21939 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
21940 }
21941 if (pDevice->wasapi.pCaptureClient) {
21942 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
21943 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
21944 pDevice->wasapi.pMappedBufferCapture = NULL;
21945 pDevice->wasapi.mappedBufferCaptureCap = 0;
21946 pDevice->wasapi.mappedBufferCaptureLen = 0;
21947 }
21948
21949 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
21950 }
21951
21952 if (pDevice->wasapi.pAudioClientPlayback) {
21953 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
21954 }
21955 if (pDevice->wasapi.pAudioClientCapture) {
21956 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
21957 }
21958
21959 if (pDevice->wasapi.hEventPlayback) {
21960 CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback);
21961 }
21962 if (pDevice->wasapi.hEventCapture) {
21963 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
21964 }
21965
21966 return MA_SUCCESS;
21967 }
21968
21969
21970 typedef struct
21971 {
21972 /* Input. */
21973 ma_format formatIn;
21974 ma_uint32 channelsIn;
21975 ma_uint32 sampleRateIn;
21976 ma_channel channelMapIn[MA_MAX_CHANNELS];
21977 ma_uint32 periodSizeInFramesIn;
21978 ma_uint32 periodSizeInMillisecondsIn;
21979 ma_uint32 periodsIn;
21980 ma_share_mode shareMode;
21981 ma_performance_profile performanceProfile;
21982 ma_bool32 noAutoConvertSRC;
21983 ma_bool32 noDefaultQualitySRC;
21984 ma_bool32 noHardwareOffloading;
21985 ma_uint32 loopbackProcessID;
21986 ma_bool32 loopbackProcessExclude;
21987
21988 /* Output. */
21989 ma_IAudioClient* pAudioClient;
21990 ma_IAudioRenderClient* pRenderClient;
21991 ma_IAudioCaptureClient* pCaptureClient;
21992 ma_format formatOut;
21993 ma_uint32 channelsOut;
21994 ma_uint32 sampleRateOut;
21995 ma_channel channelMapOut[MA_MAX_CHANNELS];
21996 ma_uint32 periodSizeInFramesOut;
21997 ma_uint32 periodsOut;
21998 ma_bool32 usingAudioClient3;
21999 char deviceName[256];
22000 ma_device_id id;
22001 } ma_device_init_internal_data__wasapi;
22002
22003 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)
22004 {
22005 HRESULT hr;
22006 ma_result result = MA_SUCCESS;
22007 const char* errorMsg = "";
22008 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
22009 DWORD streamFlags = 0;
22010 MA_REFERENCE_TIME periodDurationInMicroseconds;
22011 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
22012 MA_WAVEFORMATEXTENSIBLE wf;
22013 ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
22014 ma_IAudioClient2* pAudioClient2;
22015 ma_uint32 nativeSampleRate;
22016 ma_bool32 usingProcessLoopback = MA_FALSE;
22017
22018 MA_ASSERT(pContext != NULL);
22019 MA_ASSERT(pData != NULL);
22020
22021 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
22022 if (deviceType == ma_device_type_duplex) {
22023 return MA_INVALID_ARGS;
22024 }
22025
22026 usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL;
22027
22028 pData->pAudioClient = NULL;
22029 pData->pRenderClient = NULL;
22030 pData->pCaptureClient = NULL;
22031
22032 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
22033 if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
22034 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
22035 }
22036 if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
22037 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
22038 }
22039 if (deviceType == ma_device_type_loopback) {
22040 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
22041 }
22042
22043 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface);
22044 if (result != MA_SUCCESS) {
22045 goto done;
22046 }
22047
22048 MA_ZERO_OBJECT(&wf);
22049
22050 /* Try enabling hardware offloading. */
22051 if (!pData->noHardwareOffloading) {
22052 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
22053 if (SUCCEEDED(hr)) {
22054 BOOL isHardwareOffloadingSupported = 0;
22055 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
22056 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
22057 ma_AudioClientProperties clientProperties;
22058 MA_ZERO_OBJECT(&clientProperties);
22059 clientProperties.cbSize = sizeof(clientProperties);
22060 clientProperties.bIsOffload = 1;
22061 clientProperties.eCategory = MA_AudioCategory_Other;
22062 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
22063 }
22064
22065 pAudioClient2->lpVtbl->Release(pAudioClient2);
22066 }
22067 }
22068
22069 /* 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. */
22070 result = MA_FORMAT_NOT_SUPPORTED;
22071 if (pData->shareMode == ma_share_mode_exclusive) {
22072 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22073 /* In exclusive mode on desktop we always use the backend's native format. */
22074 ma_IPropertyStore* pStore = NULL;
22075 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
22076 if (SUCCEEDED(hr)) {
22077 MA_PROPVARIANT prop;
22078 ma_PropVariantInit(&prop);
22079 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
22080 if (SUCCEEDED(hr)) {
22081 MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData;
22082 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
22083 if (SUCCEEDED(hr)) {
22084 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
22085 }
22086
22087 ma_PropVariantClear(pContext, &prop);
22088 }
22089
22090 ma_IPropertyStore_Release(pStore);
22091 }
22092 #else
22093 /*
22094 I do not know how to query the device's native format on UWP so for now I'm just disabling support for
22095 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
22096 until you find one that works.
22097
22098 TODO: Add support for exclusive mode to UWP.
22099 */
22100 hr = S_FALSE;
22101 #endif
22102
22103 if (hr == S_OK) {
22104 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
22105 result = MA_SUCCESS;
22106 } else {
22107 result = MA_SHARE_MODE_NOT_SUPPORTED;
22108 }
22109 } else {
22110 /* In shared mode we are always using the format reported by the operating system. */
22111 MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
22112 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat);
22113 if (hr != S_OK) {
22114 /* When using process-specific loopback, GetMixFormat() seems to always fail. */
22115 if (usingProcessLoopback) {
22116 wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
22117 wf.nChannels = 2;
22118 wf.nSamplesPerSec = 44100;
22119 wf.wBitsPerSample = 32;
22120 wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
22121 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
22122 wf.cbSize = sizeof(MA_WAVEFORMATEX);
22123
22124 result = MA_SUCCESS;
22125 } else {
22126 result = MA_FORMAT_NOT_SUPPORTED;
22127 }
22128 } else {
22129 /*
22130 I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself
22131 is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE
22132 want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be
22133 safe and only copy the WAVEFORMATEX part.
22134 */
22135 if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
22136 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
22137 } else {
22138 /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */
22139 size_t cbSize = pNativeFormat->cbSize;
22140 if (cbSize == 0) {
22141 cbSize = sizeof(MA_WAVEFORMATEX);
22142 }
22143
22144 /* Make sure we don't copy more than the capacity of `wf`. */
22145 if (cbSize > sizeof(wf)) {
22146 cbSize = sizeof(wf);
22147 }
22148
22149 MA_COPY_MEMORY(&wf, pNativeFormat, cbSize);
22150 }
22151
22152 result = MA_SUCCESS;
22153 }
22154
22155 ma_CoTaskMemFree(pContext, pNativeFormat);
22156
22157 shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
22158 }
22159
22160 /* Return an error if we still haven't found a format. */
22161 if (result != MA_SUCCESS) {
22162 errorMsg = "[WASAPI] Failed to find best device mix format.";
22163 goto done;
22164 }
22165
22166 /*
22167 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
22168 WASAPI to perform the sample rate conversion.
22169 */
22170 nativeSampleRate = wf.nSamplesPerSec;
22171 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
22172 wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;
22173 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
22174 }
22175
22176 pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf);
22177 if (pData->formatOut == ma_format_unknown) {
22178 /*
22179 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
22180 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
22181 completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED.
22182 */
22183 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
22184 result = MA_SHARE_MODE_NOT_SUPPORTED;
22185 } else {
22186 result = MA_FORMAT_NOT_SUPPORTED;
22187 }
22188
22189 errorMsg = "[WASAPI] Native format not supported.";
22190 goto done;
22191 }
22192
22193 pData->channelsOut = wf.nChannels;
22194 pData->sampleRateOut = wf.nSamplesPerSec;
22195
22196 /*
22197 Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns
22198 a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this
22199 case we'll just use the default channel map.
22200 */
22201 if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) {
22202 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
22203 } else {
22204 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
22205 }
22206
22207 /* Period size. */
22208 pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;
22209 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
22210 if (pData->periodSizeInFramesOut == 0) {
22211 if (pData->periodSizeInMillisecondsIn == 0) {
22212 if (pData->performanceProfile == ma_performance_profile_low_latency) {
22213 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec);
22214 } else {
22215 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec);
22216 }
22217 } else {
22218 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec);
22219 }
22220 }
22221
22222 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec;
22223
22224
22225 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
22226 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
22227 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;
22228
22229 /*
22230 If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
22231 it and trying it again.
22232 */
22233 hr = E_FAIL;
22234 for (;;) {
22235 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
22236 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
22237 if (bufferDuration > 500*10000) {
22238 break;
22239 } else {
22240 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
22241 break;
22242 }
22243
22244 bufferDuration = bufferDuration * 2;
22245 continue;
22246 }
22247 } else {
22248 break;
22249 }
22250 }
22251
22252 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
22253 ma_uint32 bufferSizeInFrames;
22254 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
22255 if (SUCCEEDED(hr)) {
22256 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5);
22257
22258 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
22259 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
22260
22261 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22262 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
22263 #else
22264 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
22265 #endif
22266
22267 if (SUCCEEDED(hr)) {
22268 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
22269 }
22270 }
22271 }
22272
22273 if (FAILED(hr)) {
22274 /* 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. */
22275 if (hr == E_ACCESSDENIED) {
22276 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
22277 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
22278 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
22279 } else {
22280 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
22281 }
22282 goto done;
22283 }
22284 }
22285
22286 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
22287 /*
22288 Low latency shared mode via IAudioClient3.
22289
22290 NOTE
22291 ====
22292 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
22293 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
22294 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
22295 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
22296 */
22297 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
22298 {
22299 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) {
22300 ma_IAudioClient3* pAudioClient3 = NULL;
22301 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
22302 if (SUCCEEDED(hr)) {
22303 ma_uint32 defaultPeriodInFrames;
22304 ma_uint32 fundamentalPeriodInFrames;
22305 ma_uint32 minPeriodInFrames;
22306 ma_uint32 maxPeriodInFrames;
22307 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
22308 if (SUCCEEDED(hr)) {
22309 ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
22310 ma_uint32 actualPeriodInFrames = desiredPeriodInFrames;
22311
22312 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
22313 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
22314 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
22315
22316 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
22317 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
22318
22319 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
22320 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
22321 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
22322 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames);
22323 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames);
22324
22325 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
22326 if (actualPeriodInFrames >= desiredPeriodInFrames) {
22327 /*
22328 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
22329 IAudioClient3_InitializeSharedAudioStream() will fail.
22330 */
22331 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL);
22332 if (SUCCEEDED(hr)) {
22333 wasInitializedUsingIAudioClient3 = MA_TRUE;
22334 pData->periodSizeInFramesOut = actualPeriodInFrames;
22335
22336 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n");
22337 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
22338 } else {
22339 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
22340 }
22341 } else {
22342 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");
22343 }
22344 } else {
22345 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
22346 }
22347
22348 ma_IAudioClient3_Release(pAudioClient3);
22349 pAudioClient3 = NULL;
22350 }
22351 }
22352 }
22353 #else
22354 {
22355 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");
22356 }
22357 #endif
22358
22359 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
22360 if (!wasInitializedUsingIAudioClient3) {
22361 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
22362 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL);
22363 if (FAILED(hr)) {
22364 if (hr == E_ACCESSDENIED) {
22365 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
22366 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
22367 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
22368 } else {
22369 errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
22370 }
22371
22372 goto done;
22373 }
22374 }
22375 }
22376
22377 if (!wasInitializedUsingIAudioClient3) {
22378 ma_uint32 bufferSizeInFrames = 0;
22379 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
22380 if (FAILED(hr)) {
22381 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
22382 goto done;
22383 }
22384
22385 /*
22386 When using process loopback mode, retrieval of the buffer size seems to result in totally
22387 incorrect values. In this case we'll just assume it's the same size as what we requested
22388 when we initialized the client.
22389 */
22390 if (usingProcessLoopback) {
22391 bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000);
22392 }
22393
22394 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
22395 }
22396
22397 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
22398
22399
22400 if (deviceType == ma_device_type_playback) {
22401 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
22402 } else {
22403 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);
22404 }
22405
22406 /*if (FAILED(hr)) {*/
22407 if (result != MA_SUCCESS) {
22408 errorMsg = "[WASAPI] Failed to get audio client service.";
22409 goto done;
22410 }
22411
22412
22413 /* Grab the name of the device. */
22414 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22415 {
22416 ma_IPropertyStore *pProperties;
22417 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
22418 if (SUCCEEDED(hr)) {
22419 MA_PROPVARIANT varName;
22420 ma_PropVariantInit(&varName);
22421 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
22422 if (SUCCEEDED(hr)) {
22423 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
22424 ma_PropVariantClear(pContext, &varName);
22425 }
22426
22427 ma_IPropertyStore_Release(pProperties);
22428 }
22429 }
22430 #endif
22431
22432 /*
22433 For the WASAPI backend we need to know the actual IDs of the device in order to do automatic
22434 stream routing so that IDs can be compared and we can determine which device has been detached
22435 and whether or not it matches with our ma_device.
22436 */
22437 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22438 {
22439 /* Desktop */
22440 ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);
22441 }
22442 #else
22443 {
22444 /* UWP */
22445 /* TODO: Implement me. Need to figure out how to get the ID of the default device. */
22446 }
22447 #endif
22448
22449 done:
22450 /* Clean up. */
22451 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22452 if (pDeviceInterface != NULL) {
22453 ma_IMMDevice_Release(pDeviceInterface);
22454 }
22455 #else
22456 if (pDeviceInterface != NULL) {
22457 ma_IUnknown_Release(pDeviceInterface);
22458 }
22459 #endif
22460
22461 if (result != MA_SUCCESS) {
22462 if (pData->pRenderClient) {
22463 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
22464 pData->pRenderClient = NULL;
22465 }
22466 if (pData->pCaptureClient) {
22467 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
22468 pData->pCaptureClient = NULL;
22469 }
22470 if (pData->pAudioClient) {
22471 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
22472 pData->pAudioClient = NULL;
22473 }
22474
22475 if (errorMsg != NULL && errorMsg[0] != '\0') {
22476 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg);
22477 }
22478
22479 return result;
22480 } else {
22481 return MA_SUCCESS;
22482 }
22483 }
22484
22485 static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
22486 {
22487 ma_device_init_internal_data__wasapi data;
22488 ma_result result;
22489
22490 MA_ASSERT(pDevice != NULL);
22491
22492 /* We only re-initialize the playback or capture device. Never a full-duplex device. */
22493 if (deviceType == ma_device_type_duplex) {
22494 return MA_INVALID_ARGS;
22495 }
22496
22497
22498 /*
22499 Before reinitializing the device we need to free the previous audio clients.
22500
22501 There's a known memory leak here. We will be calling this from the routing change callback that
22502 is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion
22503 this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably
22504 need some system where we post an event, but delay the execution of it until the callback has
22505 returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for
22506 a command thread which might be useful for this.
22507 */
22508 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
22509 if (pDevice->wasapi.pCaptureClient) {
22510 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22511 pDevice->wasapi.pCaptureClient = NULL;
22512 }
22513
22514 if (pDevice->wasapi.pAudioClientCapture) {
22515 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/
22516 pDevice->wasapi.pAudioClientCapture = NULL;
22517 }
22518 }
22519
22520 if (deviceType == ma_device_type_playback) {
22521 if (pDevice->wasapi.pRenderClient) {
22522 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
22523 pDevice->wasapi.pRenderClient = NULL;
22524 }
22525
22526 if (pDevice->wasapi.pAudioClientPlayback) {
22527 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/
22528 pDevice->wasapi.pAudioClientPlayback = NULL;
22529 }
22530 }
22531
22532
22533 if (deviceType == ma_device_type_playback) {
22534 data.formatIn = pDevice->playback.format;
22535 data.channelsIn = pDevice->playback.channels;
22536 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
22537 data.shareMode = pDevice->playback.shareMode;
22538 } else {
22539 data.formatIn = pDevice->capture.format;
22540 data.channelsIn = pDevice->capture.channels;
22541 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
22542 data.shareMode = pDevice->capture.shareMode;
22543 }
22544
22545 data.sampleRateIn = pDevice->sampleRate;
22546 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
22547 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
22548 data.periodsIn = pDevice->wasapi.originalPeriods;
22549 data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
22550 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
22551 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
22552 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
22553 data.loopbackProcessID = pDevice->wasapi.loopbackProcessID;
22554 data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude;
22555 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
22556 if (result != MA_SUCCESS) {
22557 return result;
22558 }
22559
22560 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
22561 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
22562 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
22563 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
22564
22565 pDevice->capture.internalFormat = data.formatOut;
22566 pDevice->capture.internalChannels = data.channelsOut;
22567 pDevice->capture.internalSampleRate = data.sampleRateOut;
22568 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22569 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22570 pDevice->capture.internalPeriods = data.periodsOut;
22571 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
22572
22573 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
22574
22575 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
22576 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
22577
22578 /* We must always have a valid ID. */
22579 ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
22580 }
22581
22582 if (deviceType == ma_device_type_playback) {
22583 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
22584 pDevice->wasapi.pRenderClient = data.pRenderClient;
22585
22586 pDevice->playback.internalFormat = data.formatOut;
22587 pDevice->playback.internalChannels = data.channelsOut;
22588 pDevice->playback.internalSampleRate = data.sampleRateOut;
22589 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
22590 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
22591 pDevice->playback.internalPeriods = data.periodsOut;
22592 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
22593
22594 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
22595
22596 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
22597 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
22598
22599 /* We must always have a valid ID because rerouting will look at it. */
22600 ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
22601 }
22602
22603 return MA_SUCCESS;
22604 }
22605
22606 static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
22607 {
22608 ma_result result = MA_SUCCESS;
22609
22610 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22611 HRESULT hr;
22612 ma_IMMDeviceEnumerator* pDeviceEnumerator;
22613 #endif
22614
22615 MA_ASSERT(pDevice != NULL);
22616
22617 MA_ZERO_OBJECT(&pDevice->wasapi);
22618 pDevice->wasapi.usage = pConfig->wasapi.usage;
22619 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
22620 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
22621 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
22622 pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
22623 pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
22624
22625 /* Exclusive mode is not allowed with loopback. */
22626 if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
22627 return MA_INVALID_DEVICE_CONFIG;
22628 }
22629
22630 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
22631 ma_device_init_internal_data__wasapi data;
22632 data.formatIn = pDescriptorCapture->format;
22633 data.channelsIn = pDescriptorCapture->channels;
22634 data.sampleRateIn = pDescriptorCapture->sampleRate;
22635 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
22636 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
22637 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
22638 data.periodsIn = pDescriptorCapture->periodCount;
22639 data.shareMode = pDescriptorCapture->shareMode;
22640 data.performanceProfile = pConfig->performanceProfile;
22641 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
22642 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
22643 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
22644 data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
22645 data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
22646
22647 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);
22648 if (result != MA_SUCCESS) {
22649 return result;
22650 }
22651
22652 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
22653 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
22654 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
22655 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
22656 pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
22657 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
22658
22659 /*
22660 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
22661 however, because we want to block until we actually have something for the first call to ma_device_read().
22662 */
22663 pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
22664 if (pDevice->wasapi.hEventCapture == NULL) {
22665 result = ma_result_from_GetLastError(GetLastError());
22666
22667 if (pDevice->wasapi.pCaptureClient != NULL) {
22668 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22669 pDevice->wasapi.pCaptureClient = NULL;
22670 }
22671 if (pDevice->wasapi.pAudioClientCapture != NULL) {
22672 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22673 pDevice->wasapi.pAudioClientCapture = NULL;
22674 }
22675
22676 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.");
22677 return result;
22678 }
22679 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
22680
22681 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
22682 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
22683
22684 /* We must always have a valid ID. */
22685 ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
22686
22687 /* The descriptor needs to be updated with actual values. */
22688 pDescriptorCapture->format = data.formatOut;
22689 pDescriptorCapture->channels = data.channelsOut;
22690 pDescriptorCapture->sampleRate = data.sampleRateOut;
22691 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
22692 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
22693 pDescriptorCapture->periodCount = data.periodsOut;
22694 }
22695
22696 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22697 ma_device_init_internal_data__wasapi data;
22698 data.formatIn = pDescriptorPlayback->format;
22699 data.channelsIn = pDescriptorPlayback->channels;
22700 data.sampleRateIn = pDescriptorPlayback->sampleRate;
22701 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
22702 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
22703 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
22704 data.periodsIn = pDescriptorPlayback->periodCount;
22705 data.shareMode = pDescriptorPlayback->shareMode;
22706 data.performanceProfile = pConfig->performanceProfile;
22707 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
22708 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
22709 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
22710 data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
22711 data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
22712
22713 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
22714 if (result != MA_SUCCESS) {
22715 if (pConfig->deviceType == ma_device_type_duplex) {
22716 if (pDevice->wasapi.pCaptureClient != NULL) {
22717 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22718 pDevice->wasapi.pCaptureClient = NULL;
22719 }
22720 if (pDevice->wasapi.pAudioClientCapture != NULL) {
22721 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22722 pDevice->wasapi.pAudioClientCapture = NULL;
22723 }
22724
22725 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
22726 pDevice->wasapi.hEventCapture = NULL;
22727 }
22728 return result;
22729 }
22730
22731 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
22732 pDevice->wasapi.pRenderClient = data.pRenderClient;
22733 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
22734 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
22735 pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
22736 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
22737
22738 /*
22739 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
22740 only after the whole available space has been filled, never before.
22741
22742 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
22743 to get passed WaitForMultipleObjects().
22744 */
22745 pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
22746 if (pDevice->wasapi.hEventPlayback == NULL) {
22747 result = ma_result_from_GetLastError(GetLastError());
22748
22749 if (pConfig->deviceType == ma_device_type_duplex) {
22750 if (pDevice->wasapi.pCaptureClient != NULL) {
22751 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
22752 pDevice->wasapi.pCaptureClient = NULL;
22753 }
22754 if (pDevice->wasapi.pAudioClientCapture != NULL) {
22755 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22756 pDevice->wasapi.pAudioClientCapture = NULL;
22757 }
22758
22759 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
22760 pDevice->wasapi.hEventCapture = NULL;
22761 }
22762
22763 if (pDevice->wasapi.pRenderClient != NULL) {
22764 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
22765 pDevice->wasapi.pRenderClient = NULL;
22766 }
22767 if (pDevice->wasapi.pAudioClientPlayback != NULL) {
22768 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
22769 pDevice->wasapi.pAudioClientPlayback = NULL;
22770 }
22771
22772 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.");
22773 return result;
22774 }
22775 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
22776
22777 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
22778 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
22779
22780 /* We must always have a valid ID because rerouting will look at it. */
22781 ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
22782
22783 /* The descriptor needs to be updated with actual values. */
22784 pDescriptorPlayback->format = data.formatOut;
22785 pDescriptorPlayback->channels = data.channelsOut;
22786 pDescriptorPlayback->sampleRate = data.sampleRateOut;
22787 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
22788 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
22789 pDescriptorPlayback->periodCount = data.periodsOut;
22790 }
22791
22792 /*
22793 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
22794 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
22795 stop the device outright and let the application handle it.
22796 */
22797 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22798 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
22799 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) {
22800 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
22801 }
22802 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
22803 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
22804 }
22805 }
22806
22807 ma_mutex_init(&pDevice->wasapi.rerouteLock);
22808
22809 hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
22810 if (FAILED(hr)) {
22811 ma_device_uninit__wasapi(pDevice);
22812 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
22813 return ma_result_from_HRESULT(hr);
22814 }
22815
22816 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
22817 pDevice->wasapi.notificationClient.counter = 1;
22818 pDevice->wasapi.notificationClient.pDevice = pDevice;
22819
22820 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
22821 if (SUCCEEDED(hr)) {
22822 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
22823 } else {
22824 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
22825 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
22826 }
22827 #endif
22828
22829 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
22830 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
22831
22832 return MA_SUCCESS;
22833 }
22834
22835 static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
22836 {
22837 ma_uint32 paddingFramesCount;
22838 HRESULT hr;
22839 ma_share_mode shareMode;
22840
22841 MA_ASSERT(pDevice != NULL);
22842 MA_ASSERT(pFrameCount != NULL);
22843
22844 *pFrameCount = 0;
22845
22846 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
22847 return MA_INVALID_OPERATION;
22848 }
22849
22850 /*
22851 I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing
22852 higher level function calls from doing anything because it thinks nothing is available. I have
22853 taken a look at the documentation and it looks like this is unnecessary in exclusive mode.
22854
22855 From Microsoft's documentation:
22856
22857 For an exclusive-mode rendering or capture stream that was initialized with the
22858 AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding
22859 value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during
22860 each processing pass.
22861
22862 Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the
22863 entire buffer. This depends on the caller making sure they wait on the event handler.
22864 */
22865 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
22866 if (shareMode == ma_share_mode_shared) {
22867 /* Shared mode. */
22868 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
22869 if (FAILED(hr)) {
22870 return ma_result_from_HRESULT(hr);
22871 }
22872
22873 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
22874 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;
22875 } else {
22876 *pFrameCount = paddingFramesCount;
22877 }
22878 } else {
22879 /* Exclusive mode. */
22880 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
22881 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback;
22882 } else {
22883 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture;
22884 }
22885 }
22886
22887 return MA_SUCCESS;
22888 }
22889
22890
22891 static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
22892 {
22893 ma_result result;
22894
22895 if (deviceType == ma_device_type_duplex) {
22896 return MA_INVALID_ARGS;
22897 }
22898
22899 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n");
22900
22901 result = ma_device_reinit__wasapi(pDevice, deviceType);
22902 if (result != MA_SUCCESS) {
22903 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n");
22904 return result;
22905 }
22906
22907 ma_device__post_init_setup(pDevice, deviceType);
22908 ma_device__on_notification_rerouted(pDevice);
22909
22910 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n");
22911
22912 return MA_SUCCESS;
22913 }
22914
22915 static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice)
22916 {
22917 HRESULT hr;
22918
22919 if (pDevice->pContext->wasapi.hAvrt) {
22920 const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage);
22921 if (pTaskName) {
22922 DWORD idx = 0;
22923 pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx);
22924 }
22925 }
22926
22927 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
22928 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22929 if (FAILED(hr)) {
22930 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr);
22931 return ma_result_from_HRESULT(hr);
22932 }
22933
22934 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE);
22935 }
22936
22937 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22938 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
22939 if (FAILED(hr)) {
22940 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr);
22941 return ma_result_from_HRESULT(hr);
22942 }
22943
22944 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
22945 }
22946
22947 return MA_SUCCESS;
22948 }
22949
22950 static ma_result ma_device_start__wasapi(ma_device* pDevice)
22951 {
22952 ma_result result;
22953
22954 MA_ASSERT(pDevice != NULL);
22955
22956 /* Wait for any rerouting to finish before attempting to start the device. */
22957 ma_mutex_lock(&pDevice->wasapi.rerouteLock);
22958 {
22959 result = ma_device_start__wasapi_nolock(pDevice);
22960 }
22961 ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
22962
22963 return result;
22964 }
22965
22966 static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
22967 {
22968 ma_result result;
22969 HRESULT hr;
22970
22971 MA_ASSERT(pDevice != NULL);
22972
22973 if (pDevice->wasapi.hAvrtHandle) {
22974 ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle);
22975 pDevice->wasapi.hAvrtHandle = NULL;
22976 }
22977
22978 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
22979 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22980 if (FAILED(hr)) {
22981 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.");
22982 return ma_result_from_HRESULT(hr);
22983 }
22984
22985 /* The audio client needs to be reset otherwise restarting will fail. */
22986 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
22987 if (FAILED(hr)) {
22988 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.");
22989 return ma_result_from_HRESULT(hr);
22990 }
22991
22992 /* If we have a mapped buffer we need to release it. */
22993 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
22994 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
22995 pDevice->wasapi.pMappedBufferCapture = NULL;
22996 pDevice->wasapi.mappedBufferCaptureCap = 0;
22997 pDevice->wasapi.mappedBufferCaptureLen = 0;
22998 }
22999
23000 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
23001 }
23002
23003 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23004 /*
23005 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
23006 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
23007 */
23008 if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) {
23009 /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
23010 DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate;
23011
23012 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
23013 WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
23014 }
23015 else {
23016 ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
23017 ma_uint32 framesAvailablePlayback;
23018 for (;;) {
23019 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
23020 if (result != MA_SUCCESS) {
23021 break;
23022 }
23023
23024 if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {
23025 break;
23026 }
23027
23028 /*
23029 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
23030 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.
23031 */
23032 if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
23033 break;
23034 }
23035 prevFramesAvaialablePlayback = framesAvailablePlayback;
23036
23037 WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000);
23038 ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */
23039 }
23040 }
23041 }
23042
23043 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
23044 if (FAILED(hr)) {
23045 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.");
23046 return ma_result_from_HRESULT(hr);
23047 }
23048
23049 /* The audio client needs to be reset otherwise restarting will fail. */
23050 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
23051 if (FAILED(hr)) {
23052 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.");
23053 return ma_result_from_HRESULT(hr);
23054 }
23055
23056 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
23057 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
23058 pDevice->wasapi.pMappedBufferPlayback = NULL;
23059 pDevice->wasapi.mappedBufferPlaybackCap = 0;
23060 pDevice->wasapi.mappedBufferPlaybackLen = 0;
23061 }
23062
23063 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
23064 }
23065
23066 return MA_SUCCESS;
23067 }
23068
23069 static ma_result ma_device_stop__wasapi(ma_device* pDevice)
23070 {
23071 ma_result result;
23072
23073 MA_ASSERT(pDevice != NULL);
23074
23075 /* Wait for any rerouting to finish before attempting to stop the device. */
23076 ma_mutex_lock(&pDevice->wasapi.rerouteLock);
23077 {
23078 result = ma_device_stop__wasapi_nolock(pDevice);
23079 }
23080 ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
23081
23082 return result;
23083 }
23084
23085
23086 #ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS
23087 #define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000
23088 #endif
23089
23090 static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
23091 {
23092 ma_result result = MA_SUCCESS;
23093 ma_uint32 totalFramesProcessed = 0;
23094
23095 /*
23096 When reading, we need to get a buffer and process all of it before releasing it. Because the
23097 frame count (frameCount) can be different to the size of the buffer, we'll need to cache the
23098 pointer to the buffer.
23099 */
23100
23101 /* Keep running until we've processed the requested number of frames. */
23102 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
23103 ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
23104
23105 /* If we have a mapped data buffer, consume that first. */
23106 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
23107 /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */
23108 ma_uint32 framesToProcessNow = framesRemaining;
23109 if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) {
23110 framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen;
23111 }
23112
23113 /* Now just copy the data over to the output buffer. */
23114 ma_copy_pcm_frames(
23115 ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
23116 ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
23117 framesToProcessNow,
23118 pDevice->capture.internalFormat, pDevice->capture.internalChannels
23119 );
23120
23121 totalFramesProcessed += framesToProcessNow;
23122 pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow;
23123
23124 /* If the data buffer has been fully consumed we need to release it. */
23125 if (pDevice->wasapi.mappedBufferCaptureLen == 0) {
23126 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23127 pDevice->wasapi.pMappedBufferCapture = NULL;
23128 pDevice->wasapi.mappedBufferCaptureCap = 0;
23129 }
23130 } else {
23131 /* We don't have any cached data pointer, so grab another one. */
23132 HRESULT hr;
23133 DWORD flags = 0;
23134
23135 /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */
23136 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
23137 if (hr == S_OK) {
23138 /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */
23139 pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
23140
23141 /*
23142 There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every
23143 call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially
23144 work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution
23145 would be to figure out why the flag is always getting reported.
23146 */
23147 #if defined(MA_DEBUG_OUTPUT)
23148 {
23149 if (flags != 0) {
23150 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags);
23151
23152 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
23153 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);
23154 }
23155 }
23156 }
23157 #endif
23158
23159 /* Overrun detection. */
23160 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
23161 /* Glitched. Probably due to an overrun. */
23162
23163 /*
23164 If we got an overrun it probably means we're straddling the end of the buffer. In normal capture
23165 mode this is the fault of the client application because they're responsible for ensuring data is
23166 processed fast enough. In duplex mode, however, the processing of audio is tied to the playback
23167 device, so this can possibly be the result of a timing de-sync.
23168
23169 In capture mode we're not going to do any kind of recovery because the real fix is for the client
23170 application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers
23171 to prevent a never-ending sequence of glitches due to straddling the end of the buffer.
23172 */
23173 if (pDevice->type == ma_device_type_duplex) {
23174 /*
23175 Experiment:
23176
23177 If we empty out the *entire* buffer we may end up putting ourselves into an underrun position
23178 which isn't really any better than the overrun we're probably in right now. Instead we'll just
23179 empty out about half.
23180 */
23181 ma_uint32 i;
23182 ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture);
23183 ma_uint32 iterationCount = periodCount / 2;
23184 if ((periodCount % 2) > 0) {
23185 iterationCount += 1;
23186 }
23187
23188 for (i = 0; i < iterationCount; i += 1) {
23189 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23190 if (FAILED(hr)) {
23191 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr);
23192 break;
23193 }
23194
23195 flags = 0;
23196 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
23197 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) {
23198 /*
23199 The buffer has been completely emptied or an error occurred. In this case we'll need
23200 to reset the state of the mapped buffer which will trigger the next iteration to get
23201 a fresh buffer from WASAPI.
23202 */
23203 pDevice->wasapi.pMappedBufferCapture = NULL;
23204 pDevice->wasapi.mappedBufferCaptureCap = 0;
23205 pDevice->wasapi.mappedBufferCaptureLen = 0;
23206
23207 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) {
23208 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
23209 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n");
23210 } else {
23211 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n");
23212 }
23213 }
23214
23215 if (FAILED(hr)) {
23216 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr);
23217 }
23218
23219 break;
23220 }
23221 }
23222
23223 /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */
23224 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
23225 pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
23226 }
23227 }
23228 }
23229
23230 continue;
23231 } else {
23232 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
23233 /*
23234 No data is available. We need to wait for more. There's two situations to consider
23235 here. The first is normal capture mode. If this times out it probably means the
23236 microphone isn't delivering data for whatever reason. In this case we'll just
23237 abort the read and return whatever we were able to get. The other situations is
23238 loopback mode, in which case a timeout probably just means the nothing is playing
23239 through the speakers.
23240 */
23241
23242 /* Experiment: Use a shorter timeout for loopback mode. */
23243 DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS;
23244 if (pDevice->type == ma_device_type_loopback) {
23245 timeoutInMilliseconds = 10;
23246 }
23247
23248 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) {
23249 if (pDevice->type == ma_device_type_loopback) {
23250 continue; /* Keep waiting in loopback mode. */
23251 } else {
23252 result = MA_ERROR;
23253 break; /* Wait failed. */
23254 }
23255 }
23256
23257 /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */
23258 } else {
23259 /* An error occured and we need to abort. */
23260 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);
23261 result = ma_result_from_HRESULT(hr);
23262 break;
23263 }
23264 }
23265 }
23266 }
23267
23268 /*
23269 If we were unable to process the entire requested frame count, but we still have a mapped buffer,
23270 there's a good chance either an error occurred or the device was stopped mid-read. In this case
23271 we'll need to make sure the buffer is released.
23272 */
23273 if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) {
23274 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23275 pDevice->wasapi.pMappedBufferCapture = NULL;
23276 pDevice->wasapi.mappedBufferCaptureCap = 0;
23277 pDevice->wasapi.mappedBufferCaptureLen = 0;
23278 }
23279
23280 if (pFramesRead != NULL) {
23281 *pFramesRead = totalFramesProcessed;
23282 }
23283
23284 return result;
23285 }
23286
23287 static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
23288 {
23289 ma_result result = MA_SUCCESS;
23290 ma_uint32 totalFramesProcessed = 0;
23291
23292 /* Keep writing to the device until it's stopped or we've consumed all of our input. */
23293 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
23294 ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
23295
23296 /*
23297 We're going to do this in a similar way to capture. We'll first check if the cached data pointer
23298 is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with
23299 a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE
23300 it means we need to wait for some data to become available.
23301 */
23302 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
23303 /* We still have some space available in the mapped data buffer. Write to it. */
23304 ma_uint32 framesToProcessNow = framesRemaining;
23305 if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) {
23306 framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen);
23307 }
23308
23309 /* Now just copy the data over to the output buffer. */
23310 ma_copy_pcm_frames(
23311 ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
23312 ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
23313 framesToProcessNow,
23314 pDevice->playback.internalFormat, pDevice->playback.internalChannels
23315 );
23316
23317 totalFramesProcessed += framesToProcessNow;
23318 pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow;
23319
23320 /* If the data buffer has been fully consumed we need to release it. */
23321 if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) {
23322 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
23323 pDevice->wasapi.pMappedBufferPlayback = NULL;
23324 pDevice->wasapi.mappedBufferPlaybackCap = 0;
23325 pDevice->wasapi.mappedBufferPlaybackLen = 0;
23326
23327 /*
23328 In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never
23329 seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine
23330 whether or not we need to wait for more data.
23331 */
23332 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
23333 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
23334 result = MA_ERROR;
23335 break; /* Wait failed. Probably timed out. */
23336 }
23337 }
23338 }
23339 } else {
23340 /* We don't have a mapped data buffer so we'll need to get one. */
23341 HRESULT hr;
23342 ma_uint32 bufferSizeInFrames;
23343
23344 /* Special rules for exclusive mode. */
23345 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
23346 bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback;
23347 } else {
23348 bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback;
23349 }
23350
23351 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback);
23352 if (hr == S_OK) {
23353 /* We have data available. */
23354 pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames;
23355 pDevice->wasapi.mappedBufferPlaybackLen = 0;
23356 } else {
23357 if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
23358 /* Not enough data available. We need to wait for more. */
23359 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
23360 result = MA_ERROR;
23361 break; /* Wait failed. Probably timed out. */
23362 }
23363 } else {
23364 /* Some error occurred. We'll need to abort. */
23365 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);
23366 result = ma_result_from_HRESULT(hr);
23367 break;
23368 }
23369 }
23370 }
23371 }
23372
23373 if (pFramesWritten != NULL) {
23374 *pFramesWritten = totalFramesProcessed;
23375 }
23376
23377 return result;
23378 }
23379
23380 static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
23381 {
23382 MA_ASSERT(pDevice != NULL);
23383
23384 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
23385 SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
23386 }
23387
23388 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23389 SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
23390 }
23391
23392 return MA_SUCCESS;
23393 }
23394
23395
23396 static ma_result ma_context_uninit__wasapi(ma_context* pContext)
23397 {
23398 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
23399
23400 MA_ASSERT(pContext != NULL);
23401 MA_ASSERT(pContext->backend == ma_backend_wasapi);
23402
23403 ma_context_post_command__wasapi(pContext, &cmd);
23404 ma_thread_wait(&pContext->wasapi.commandThread);
23405
23406 if (pContext->wasapi.hAvrt) {
23407 ma_dlclose(pContext, pContext->wasapi.hAvrt);
23408 pContext->wasapi.hAvrt = NULL;
23409 }
23410
23411 #if defined(MA_WIN32_UWP)
23412 {
23413 if (pContext->wasapi.hMMDevapi) {
23414 ma_dlclose(pContext, pContext->wasapi.hMMDevapi);
23415 pContext->wasapi.hMMDevapi = NULL;
23416 }
23417 }
23418 #endif
23419
23420 /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */
23421 ma_semaphore_uninit(&pContext->wasapi.commandSem);
23422 ma_mutex_uninit(&pContext->wasapi.commandLock);
23423
23424 return MA_SUCCESS;
23425 }
23426
23427 static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
23428 {
23429 ma_result result = MA_SUCCESS;
23430
23431 MA_ASSERT(pContext != NULL);
23432
23433 (void)pConfig;
23434
23435 #ifdef MA_WIN32_DESKTOP
23436 /*
23437 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
23438 exclusive mode does not work until SP1.
23439
23440 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.
23441 */
23442 {
23443 ma_OSVERSIONINFOEXW osvi;
23444 ma_handle kernel32DLL;
23445 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
23446 ma_PFNVerSetConditionMask _VerSetConditionMask;
23447
23448 kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
23449 if (kernel32DLL == NULL) {
23450 return MA_NO_BACKEND;
23451 }
23452
23453 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
23454 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
23455 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
23456 ma_dlclose(pContext, kernel32DLL);
23457 return MA_NO_BACKEND;
23458 }
23459
23460 MA_ZERO_OBJECT(&osvi);
23461 osvi.dwOSVersionInfoSize = sizeof(osvi);
23462 osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
23463 osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
23464 osvi.wServicePackMajor = 1;
23465 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))) {
23466 result = MA_SUCCESS;
23467 } else {
23468 result = MA_NO_BACKEND;
23469 }
23470
23471 ma_dlclose(pContext, kernel32DLL);
23472 }
23473 #endif
23474
23475 if (result != MA_SUCCESS) {
23476 return result;
23477 }
23478
23479 MA_ZERO_OBJECT(&pContext->wasapi);
23480
23481 /*
23482 Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread
23483 than the one that retrieved it with GetService(). This can result in a deadlock in two
23484 situations:
23485
23486 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and
23487 2) When uninitializing and reinitializing the internal IAudioClient object in response to
23488 automatic stream routing.
23489
23490 We could define ma_device_uninit() such that it must be called on the same thread as
23491 ma_device_init(). We could also just not release the IAudioClient when performing automatic
23492 stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so
23493 we're going to have to work around this with a worker thread. This is not ideal, but I can't
23494 think of a better way to do this.
23495
23496 More information about this can be found here:
23497
23498 https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient
23499
23500 Note this section:
23501
23502 When releasing an IAudioRenderClient interface instance, the client must call the interface's
23503 Release method from the same thread as the call to IAudioClient::GetService that created the
23504 object.
23505 */
23506 {
23507 result = ma_mutex_init(&pContext->wasapi.commandLock);
23508 if (result != MA_SUCCESS) {
23509 return result;
23510 }
23511
23512 result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
23513 if (result != MA_SUCCESS) {
23514 ma_mutex_uninit(&pContext->wasapi.commandLock);
23515 return result;
23516 }
23517
23518 result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
23519 if (result != MA_SUCCESS) {
23520 ma_semaphore_uninit(&pContext->wasapi.commandSem);
23521 ma_mutex_uninit(&pContext->wasapi.commandLock);
23522 return result;
23523 }
23524
23525 #if defined(MA_WIN32_UWP)
23526 {
23527 /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */
23528 pContext->wasapi.hMMDevapi = ma_dlopen(pContext, "mmdevapi.dll");
23529 if (pContext->wasapi.hMMDevapi) {
23530 pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(pContext, pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync");
23531 if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) {
23532 ma_semaphore_uninit(&pContext->wasapi.commandSem);
23533 ma_mutex_uninit(&pContext->wasapi.commandLock);
23534 ma_dlclose(pContext, pContext->wasapi.hMMDevapi);
23535 return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */
23536 }
23537 } else {
23538 ma_semaphore_uninit(&pContext->wasapi.commandSem);
23539 ma_mutex_uninit(&pContext->wasapi.commandLock);
23540 return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */
23541 }
23542 }
23543 #endif
23544
23545 /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */
23546 pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll");
23547 if (pContext->wasapi.hAvrt) {
23548 pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA");
23549 pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics");
23550
23551 /* If either function could not be found, disable use of avrt entirely. */
23552 if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) {
23553 pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL;
23554 pContext->wasapi.AvRevertMmThreadcharacteristics = NULL;
23555 ma_dlclose(pContext, pContext->wasapi.hAvrt);
23556 pContext->wasapi.hAvrt = NULL;
23557 }
23558 }
23559 }
23560
23561
23562 pCallbacks->onContextInit = ma_context_init__wasapi;
23563 pCallbacks->onContextUninit = ma_context_uninit__wasapi;
23564 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
23565 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
23566 pCallbacks->onDeviceInit = ma_device_init__wasapi;
23567 pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
23568 pCallbacks->onDeviceStart = ma_device_start__wasapi;
23569 pCallbacks->onDeviceStop = ma_device_stop__wasapi;
23570 pCallbacks->onDeviceRead = ma_device_read__wasapi;
23571 pCallbacks->onDeviceWrite = ma_device_write__wasapi;
23572 pCallbacks->onDeviceDataLoop = NULL;
23573 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
23574
23575 return MA_SUCCESS;
23576 }
23577 #endif
23578
23579 /******************************************************************************
23580
23581 DirectSound Backend
23582
23583 ******************************************************************************/
23584 #ifdef MA_HAS_DSOUND
23585 /*#include <dsound.h>*/
23586
23587 /*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/
23588
23589 /* miniaudio only uses priority or exclusive modes. */
23590 #define MA_DSSCL_NORMAL 1
23591 #define MA_DSSCL_PRIORITY 2
23592 #define MA_DSSCL_EXCLUSIVE 3
23593 #define MA_DSSCL_WRITEPRIMARY 4
23594
23595 #define MA_DSCAPS_PRIMARYMONO 0x00000001
23596 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002
23597 #define MA_DSCAPS_PRIMARY8BIT 0x00000004
23598 #define MA_DSCAPS_PRIMARY16BIT 0x00000008
23599 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010
23600 #define MA_DSCAPS_EMULDRIVER 0x00000020
23601 #define MA_DSCAPS_CERTIFIED 0x00000040
23602 #define MA_DSCAPS_SECONDARYMONO 0x00000100
23603 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200
23604 #define MA_DSCAPS_SECONDARY8BIT 0x00000400
23605 #define MA_DSCAPS_SECONDARY16BIT 0x00000800
23606
23607 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
23608 #define MA_DSBCAPS_STATIC 0x00000002
23609 #define MA_DSBCAPS_LOCHARDWARE 0x00000004
23610 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008
23611 #define MA_DSBCAPS_CTRL3D 0x00000010
23612 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
23613 #define MA_DSBCAPS_CTRLPAN 0x00000040
23614 #define MA_DSBCAPS_CTRLVOLUME 0x00000080
23615 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
23616 #define MA_DSBCAPS_CTRLFX 0x00000200
23617 #define MA_DSBCAPS_STICKYFOCUS 0x00004000
23618 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000
23619 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
23620 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
23621 #define MA_DSBCAPS_LOCDEFER 0x00040000
23622 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
23623
23624 #define MA_DSBPLAY_LOOPING 0x00000001
23625 #define MA_DSBPLAY_LOCHARDWARE 0x00000002
23626 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004
23627 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
23628 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
23629 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
23630
23631 #define MA_DSCBSTART_LOOPING 0x00000001
23632
23633 typedef struct
23634 {
23635 DWORD dwSize;
23636 DWORD dwFlags;
23637 DWORD dwBufferBytes;
23638 DWORD dwReserved;
23639 MA_WAVEFORMATEX* lpwfxFormat;
23640 GUID guid3DAlgorithm;
23641 } MA_DSBUFFERDESC;
23642
23643 typedef struct
23644 {
23645 DWORD dwSize;
23646 DWORD dwFlags;
23647 DWORD dwBufferBytes;
23648 DWORD dwReserved;
23649 MA_WAVEFORMATEX* lpwfxFormat;
23650 DWORD dwFXCount;
23651 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
23652 } MA_DSCBUFFERDESC;
23653
23654 typedef struct
23655 {
23656 DWORD dwSize;
23657 DWORD dwFlags;
23658 DWORD dwMinSecondarySampleRate;
23659 DWORD dwMaxSecondarySampleRate;
23660 DWORD dwPrimaryBuffers;
23661 DWORD dwMaxHwMixingAllBuffers;
23662 DWORD dwMaxHwMixingStaticBuffers;
23663 DWORD dwMaxHwMixingStreamingBuffers;
23664 DWORD dwFreeHwMixingAllBuffers;
23665 DWORD dwFreeHwMixingStaticBuffers;
23666 DWORD dwFreeHwMixingStreamingBuffers;
23667 DWORD dwMaxHw3DAllBuffers;
23668 DWORD dwMaxHw3DStaticBuffers;
23669 DWORD dwMaxHw3DStreamingBuffers;
23670 DWORD dwFreeHw3DAllBuffers;
23671 DWORD dwFreeHw3DStaticBuffers;
23672 DWORD dwFreeHw3DStreamingBuffers;
23673 DWORD dwTotalHwMemBytes;
23674 DWORD dwFreeHwMemBytes;
23675 DWORD dwMaxContigFreeHwMemBytes;
23676 DWORD dwUnlockTransferRateHwBuffers;
23677 DWORD dwPlayCpuOverheadSwBuffers;
23678 DWORD dwReserved1;
23679 DWORD dwReserved2;
23680 } MA_DSCAPS;
23681
23682 typedef struct
23683 {
23684 DWORD dwSize;
23685 DWORD dwFlags;
23686 DWORD dwBufferBytes;
23687 DWORD dwUnlockTransferRate;
23688 DWORD dwPlayCpuOverhead;
23689 } MA_DSBCAPS;
23690
23691 typedef struct
23692 {
23693 DWORD dwSize;
23694 DWORD dwFlags;
23695 DWORD dwFormats;
23696 DWORD dwChannels;
23697 } MA_DSCCAPS;
23698
23699 typedef struct
23700 {
23701 DWORD dwSize;
23702 DWORD dwFlags;
23703 DWORD dwBufferBytes;
23704 DWORD dwReserved;
23705 } MA_DSCBCAPS;
23706
23707 typedef struct
23708 {
23709 DWORD dwOffset;
23710 HANDLE hEventNotify;
23711 } MA_DSBPOSITIONNOTIFY;
23712
23713 typedef struct ma_IDirectSound ma_IDirectSound;
23714 typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
23715 typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
23716 typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
23717 typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
23718
23719
23720 /*
23721 COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
23722 like how C++ works internally), and then you have a structure with a single member, which is a
23723 pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
23724 to be in a specific order, and parent classes need to have their methods declared first.
23725 */
23726
23727 /* IDirectSound */
23728 typedef struct
23729 {
23730 /* IUnknown */
23731 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
23732 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
23733 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
23734
23735 /* IDirectSound */
23736 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
23737 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
23738 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
23739 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
23740 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
23741 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
23742 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
23743 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
23744 } ma_IDirectSoundVtbl;
23745 struct ma_IDirectSound
23746 {
23747 ma_IDirectSoundVtbl* lpVtbl;
23748 };
23749 static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23750 static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23751 static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
23752 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); }
23753 static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
23754 static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
23755 static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
23756 static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
23757 static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
23758 static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
23759 static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
23760
23761
23762 /* IDirectSoundBuffer */
23763 typedef struct
23764 {
23765 /* IUnknown */
23766 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
23767 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
23768 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
23769
23770 /* IDirectSoundBuffer */
23771 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
23772 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
23773 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
23774 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
23775 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
23776 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
23777 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
23778 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
23779 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
23780 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
23781 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
23782 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat);
23783 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
23784 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
23785 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
23786 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
23787 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
23788 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
23789 } ma_IDirectSoundBufferVtbl;
23790 struct ma_IDirectSoundBuffer
23791 {
23792 ma_IDirectSoundBufferVtbl* lpVtbl;
23793 };
23794 static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23795 static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23796 static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
23797 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
23798 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
23799 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); }
23800 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
23801 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
23802 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
23803 static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
23804 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
23805 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); }
23806 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
23807 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
23808 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
23809 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
23810 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
23811 static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
23812 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
23813 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); }
23814 static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
23815
23816
23817 /* IDirectSoundCapture */
23818 typedef struct
23819 {
23820 /* IUnknown */
23821 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
23822 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
23823 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
23824
23825 /* IDirectSoundCapture */
23826 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
23827 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
23828 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
23829 } ma_IDirectSoundCaptureVtbl;
23830 struct ma_IDirectSoundCapture
23831 {
23832 ma_IDirectSoundCaptureVtbl* lpVtbl;
23833 };
23834 static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23835 static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23836 static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
23837 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); }
23838 static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
23839 static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
23840
23841
23842 /* IDirectSoundCaptureBuffer */
23843 typedef struct
23844 {
23845 /* IUnknown */
23846 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
23847 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
23848 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
23849
23850 /* IDirectSoundCaptureBuffer */
23851 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
23852 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
23853 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
23854 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
23855 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
23856 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
23857 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
23858 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
23859 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
23860 } ma_IDirectSoundCaptureBufferVtbl;
23861 struct ma_IDirectSoundCaptureBuffer
23862 {
23863 ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
23864 };
23865 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23866 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23867 static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
23868 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
23869 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
23870 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); }
23871 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
23872 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
23873 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); }
23874 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
23875 static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
23876 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); }
23877
23878
23879 /* IDirectSoundNotify */
23880 typedef struct
23881 {
23882 /* IUnknown */
23883 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
23884 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
23885 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
23886
23887 /* IDirectSoundNotify */
23888 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
23889 } ma_IDirectSoundNotifyVtbl;
23890 struct ma_IDirectSoundNotify
23891 {
23892 ma_IDirectSoundNotifyVtbl* lpVtbl;
23893 };
23894 static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
23895 static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
23896 static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
23897 static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
23898
23899
23900 typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext);
23901 typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter);
23902 typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
23903 typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter);
23904 typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
23905
23906 static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
23907 {
23908 /* Normalize the range in case we were given something stupid. */
23909 if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {
23910 sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;
23911 }
23912 if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {
23913 sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;
23914 }
23915 if (sampleRateMin > sampleRateMax) {
23916 sampleRateMin = sampleRateMax;
23917 }
23918
23919 if (sampleRateMin == sampleRateMax) {
23920 return sampleRateMax;
23921 } else {
23922 size_t iStandardRate;
23923 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
23924 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
23925 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
23926 return standardRate;
23927 }
23928 }
23929 }
23930
23931 /* Should never get here. */
23932 MA_ASSERT(MA_FALSE);
23933 return 0;
23934 }
23935
23936 /*
23937 Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
23938 the channel count and channel map will be left unmodified.
23939 */
23940 static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
23941 {
23942 WORD channels;
23943 DWORD channelMap;
23944
23945 channels = 0;
23946 if (pChannelsOut != NULL) {
23947 channels = *pChannelsOut;
23948 }
23949
23950 channelMap = 0;
23951 if (pChannelMapOut != NULL) {
23952 channelMap = *pChannelMapOut;
23953 }
23954
23955 /*
23956 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
23957 16 bits is for the geometry.
23958 */
23959 switch ((BYTE)(speakerConfig)) {
23960 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
23961 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
23962 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
23963 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
23964 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
23965 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;
23966 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;
23967 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;
23968 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;
23969 default: break;
23970 }
23971
23972 if (pChannelsOut != NULL) {
23973 *pChannelsOut = channels;
23974 }
23975
23976 if (pChannelMapOut != NULL) {
23977 *pChannelMapOut = channelMap;
23978 }
23979 }
23980
23981
23982 static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
23983 {
23984 ma_IDirectSound* pDirectSound;
23985 HWND hWnd;
23986 HRESULT hr;
23987
23988 MA_ASSERT(pContext != NULL);
23989 MA_ASSERT(ppDirectSound != NULL);
23990
23991 *ppDirectSound = NULL;
23992 pDirectSound = NULL;
23993
23994 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
23995 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.");
23996 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
23997 }
23998
23999 /* The cooperative level must be set before doing anything else. */
24000 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
24001 if (hWnd == 0) {
24002 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
24003 }
24004
24005 hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
24006 if (FAILED(hr)) {
24007 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.");
24008 return ma_result_from_HRESULT(hr);
24009 }
24010
24011 *ppDirectSound = pDirectSound;
24012 return MA_SUCCESS;
24013 }
24014
24015 static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
24016 {
24017 ma_IDirectSoundCapture* pDirectSoundCapture;
24018 HRESULT hr;
24019
24020 MA_ASSERT(pContext != NULL);
24021 MA_ASSERT(ppDirectSoundCapture != NULL);
24022
24023 /* DirectSound does not support exclusive mode for capture. */
24024 if (shareMode == ma_share_mode_exclusive) {
24025 return MA_SHARE_MODE_NOT_SUPPORTED;
24026 }
24027
24028 *ppDirectSoundCapture = NULL;
24029 pDirectSoundCapture = NULL;
24030
24031 hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
24032 if (FAILED(hr)) {
24033 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.");
24034 return ma_result_from_HRESULT(hr);
24035 }
24036
24037 *ppDirectSoundCapture = pDirectSoundCapture;
24038 return MA_SUCCESS;
24039 }
24040
24041 static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
24042 {
24043 HRESULT hr;
24044 MA_DSCCAPS caps;
24045 WORD bitsPerSample;
24046 DWORD sampleRate;
24047
24048 MA_ASSERT(pContext != NULL);
24049 MA_ASSERT(pDirectSoundCapture != NULL);
24050
24051 if (pChannels) {
24052 *pChannels = 0;
24053 }
24054 if (pBitsPerSample) {
24055 *pBitsPerSample = 0;
24056 }
24057 if (pSampleRate) {
24058 *pSampleRate = 0;
24059 }
24060
24061 MA_ZERO_OBJECT(&caps);
24062 caps.dwSize = sizeof(caps);
24063 hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
24064 if (FAILED(hr)) {
24065 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.");
24066 return ma_result_from_HRESULT(hr);
24067 }
24068
24069 if (pChannels) {
24070 *pChannels = (WORD)caps.dwChannels;
24071 }
24072
24073 /* 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. */
24074 bitsPerSample = 16;
24075 sampleRate = 48000;
24076
24077 if (caps.dwChannels == 1) {
24078 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
24079 sampleRate = 48000;
24080 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
24081 sampleRate = 44100;
24082 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
24083 sampleRate = 22050;
24084 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
24085 sampleRate = 11025;
24086 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
24087 sampleRate = 96000;
24088 } else {
24089 bitsPerSample = 8;
24090 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
24091 sampleRate = 48000;
24092 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
24093 sampleRate = 44100;
24094 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
24095 sampleRate = 22050;
24096 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
24097 sampleRate = 11025;
24098 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
24099 sampleRate = 96000;
24100 } else {
24101 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
24102 }
24103 }
24104 } else if (caps.dwChannels == 2) {
24105 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
24106 sampleRate = 48000;
24107 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
24108 sampleRate = 44100;
24109 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
24110 sampleRate = 22050;
24111 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
24112 sampleRate = 11025;
24113 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
24114 sampleRate = 96000;
24115 } else {
24116 bitsPerSample = 8;
24117 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
24118 sampleRate = 48000;
24119 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
24120 sampleRate = 44100;
24121 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
24122 sampleRate = 22050;
24123 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
24124 sampleRate = 11025;
24125 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
24126 sampleRate = 96000;
24127 } else {
24128 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
24129 }
24130 }
24131 }
24132
24133 if (pBitsPerSample) {
24134 *pBitsPerSample = bitsPerSample;
24135 }
24136 if (pSampleRate) {
24137 *pSampleRate = sampleRate;
24138 }
24139
24140 return MA_SUCCESS;
24141 }
24142
24143
24144 typedef struct
24145 {
24146 ma_context* pContext;
24147 ma_device_type deviceType;
24148 ma_enum_devices_callback_proc callback;
24149 void* pUserData;
24150 ma_bool32 terminated;
24151 } ma_context_enumerate_devices_callback_data__dsound;
24152
24153 static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
24154 {
24155 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
24156 ma_device_info deviceInfo;
24157
24158 (void)lpcstrModule;
24159
24160 MA_ZERO_OBJECT(&deviceInfo);
24161
24162 /* ID. */
24163 if (lpGuid != NULL) {
24164 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
24165 } else {
24166 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
24167 deviceInfo.isDefault = MA_TRUE;
24168 }
24169
24170 /* Name / Description */
24171 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
24172
24173
24174 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
24175 MA_ASSERT(pData != NULL);
24176 pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
24177 if (pData->terminated) {
24178 return FALSE; /* Stop enumeration. */
24179 } else {
24180 return TRUE; /* Continue enumeration. */
24181 }
24182 }
24183
24184 static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24185 {
24186 ma_context_enumerate_devices_callback_data__dsound data;
24187
24188 MA_ASSERT(pContext != NULL);
24189 MA_ASSERT(callback != NULL);
24190
24191 data.pContext = pContext;
24192 data.callback = callback;
24193 data.pUserData = pUserData;
24194 data.terminated = MA_FALSE;
24195
24196 /* Playback. */
24197 if (!data.terminated) {
24198 data.deviceType = ma_device_type_playback;
24199 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
24200 }
24201
24202 /* Capture. */
24203 if (!data.terminated) {
24204 data.deviceType = ma_device_type_capture;
24205 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
24206 }
24207
24208 return MA_SUCCESS;
24209 }
24210
24211
24212 typedef struct
24213 {
24214 const ma_device_id* pDeviceID;
24215 ma_device_info* pDeviceInfo;
24216 ma_bool32 found;
24217 } ma_context_get_device_info_callback_data__dsound;
24218
24219 static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
24220 {
24221 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
24222 MA_ASSERT(pData != NULL);
24223
24224 if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
24225 /* Default device. */
24226 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
24227 pData->pDeviceInfo->isDefault = MA_TRUE;
24228 pData->found = MA_TRUE;
24229 return FALSE; /* Stop enumeration. */
24230 } else {
24231 /* Not the default device. */
24232 if (lpGuid != NULL && pData->pDeviceID != NULL) {
24233 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
24234 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
24235 pData->found = MA_TRUE;
24236 return FALSE; /* Stop enumeration. */
24237 }
24238 }
24239 }
24240
24241 (void)lpcstrModule;
24242 return TRUE;
24243 }
24244
24245 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)
24246 {
24247 ma_result result;
24248 HRESULT hr;
24249
24250 if (pDeviceID != NULL) {
24251 ma_context_get_device_info_callback_data__dsound data;
24252
24253 /* ID. */
24254 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
24255
24256 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
24257 data.pDeviceID = pDeviceID;
24258 data.pDeviceInfo = pDeviceInfo;
24259 data.found = MA_FALSE;
24260 if (deviceType == ma_device_type_playback) {
24261 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
24262 } else {
24263 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
24264 }
24265
24266 if (!data.found) {
24267 return MA_NO_DEVICE;
24268 }
24269 } else {
24270 /* 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. */
24271
24272 /* ID */
24273 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
24274
24275 /* Name / Description */
24276 if (deviceType == ma_device_type_playback) {
24277 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24278 } else {
24279 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24280 }
24281
24282 pDeviceInfo->isDefault = MA_TRUE;
24283 }
24284
24285 /* Retrieving detailed information is slightly different depending on the device type. */
24286 if (deviceType == ma_device_type_playback) {
24287 /* Playback. */
24288 ma_IDirectSound* pDirectSound;
24289 MA_DSCAPS caps;
24290 WORD channels;
24291
24292 result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);
24293 if (result != MA_SUCCESS) {
24294 return result;
24295 }
24296
24297 MA_ZERO_OBJECT(&caps);
24298 caps.dwSize = sizeof(caps);
24299 hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
24300 if (FAILED(hr)) {
24301 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
24302 return ma_result_from_HRESULT(hr);
24303 }
24304
24305
24306 /* Channels. Only a single channel count is reported for DirectSound. */
24307 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
24308 /* It supports at least stereo, but could support more. */
24309 DWORD speakerConfig;
24310
24311 channels = 2;
24312
24313 /* Look at the speaker configuration to get a better idea on the channel count. */
24314 hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
24315 if (SUCCEEDED(hr)) {
24316 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
24317 }
24318 } else {
24319 /* It does not support stereo, which means we are stuck with mono. */
24320 channels = 1;
24321 }
24322
24323
24324 /*
24325 In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel
24326 count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio
24327 in order to keep the size of this within reason.
24328 */
24329 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
24330 /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */
24331 size_t iStandardSampleRate;
24332 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
24333 ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
24334 if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {
24335 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
24336 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
24337 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
24338 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
24339 pDeviceInfo->nativeDataFormatCount += 1;
24340 }
24341 }
24342 } else {
24343 /* Only a single sample rate is supported. */
24344 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
24345 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
24346 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;
24347 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
24348 pDeviceInfo->nativeDataFormatCount += 1;
24349 }
24350
24351 ma_IDirectSound_Release(pDirectSound);
24352 } else {
24353 /*
24354 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
24355 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
24356 reporting the best format.
24357 */
24358 ma_IDirectSoundCapture* pDirectSoundCapture;
24359 WORD channels;
24360 WORD bitsPerSample;
24361 DWORD sampleRate;
24362
24363 result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);
24364 if (result != MA_SUCCESS) {
24365 return result;
24366 }
24367
24368 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
24369 if (result != MA_SUCCESS) {
24370 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
24371 return result;
24372 }
24373
24374 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
24375
24376 /* The format is always an integer format and is based on the bits per sample. */
24377 if (bitsPerSample == 8) {
24378 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
24379 } else if (bitsPerSample == 16) {
24380 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
24381 } else if (bitsPerSample == 24) {
24382 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
24383 } else if (bitsPerSample == 32) {
24384 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
24385 } else {
24386 return MA_FORMAT_NOT_SUPPORTED;
24387 }
24388
24389 pDeviceInfo->nativeDataFormats[0].channels = channels;
24390 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
24391 pDeviceInfo->nativeDataFormats[0].flags = 0;
24392 pDeviceInfo->nativeDataFormatCount = 1;
24393 }
24394
24395 return MA_SUCCESS;
24396 }
24397
24398
24399
24400 static ma_result ma_device_uninit__dsound(ma_device* pDevice)
24401 {
24402 MA_ASSERT(pDevice != NULL);
24403
24404 if (pDevice->dsound.pCaptureBuffer != NULL) {
24405 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
24406 }
24407 if (pDevice->dsound.pCapture != NULL) {
24408 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
24409 }
24410
24411 if (pDevice->dsound.pPlaybackBuffer != NULL) {
24412 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
24413 }
24414 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
24415 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
24416 }
24417 if (pDevice->dsound.pPlayback != NULL) {
24418 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
24419 }
24420
24421 return MA_SUCCESS;
24422 }
24423
24424 static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF)
24425 {
24426 GUID subformat;
24427
24428 if (format == ma_format_unknown) {
24429 format = MA_DEFAULT_FORMAT;
24430 }
24431
24432 if (channels == 0) {
24433 channels = MA_DEFAULT_CHANNELS;
24434 }
24435
24436 if (sampleRate == 0) {
24437 sampleRate = MA_DEFAULT_SAMPLE_RATE;
24438 }
24439
24440 switch (format)
24441 {
24442 case ma_format_u8:
24443 case ma_format_s16:
24444 case ma_format_s24:
24445 /*case ma_format_s24_32:*/
24446 case ma_format_s32:
24447 {
24448 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
24449 } break;
24450
24451 case ma_format_f32:
24452 {
24453 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
24454 } break;
24455
24456 default:
24457 return MA_FORMAT_NOT_SUPPORTED;
24458 }
24459
24460 MA_ZERO_OBJECT(pWF);
24461 pWF->cbSize = sizeof(*pWF);
24462 pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
24463 pWF->nChannels = (WORD)channels;
24464 pWF->nSamplesPerSec = (DWORD)sampleRate;
24465 pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
24466 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
24467 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
24468 pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample;
24469 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
24470 pWF->SubFormat = subformat;
24471
24472 return MA_SUCCESS;
24473 }
24474
24475 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
24476 {
24477 /*
24478 DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for
24479 reliable glitch-free processing so going to use 30ms instead.
24480 */
24481 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate);
24482 ma_uint32 periodSizeInFrames;
24483
24484 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
24485 if (periodSizeInFrames < minPeriodSizeInFrames) {
24486 periodSizeInFrames = minPeriodSizeInFrames;
24487 }
24488
24489 return periodSizeInFrames;
24490 }
24491
24492 static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
24493 {
24494 ma_result result;
24495 HRESULT hr;
24496
24497 MA_ASSERT(pDevice != NULL);
24498
24499 MA_ZERO_OBJECT(&pDevice->dsound);
24500
24501 if (pConfig->deviceType == ma_device_type_loopback) {
24502 return MA_DEVICE_TYPE_NOT_SUPPORTED;
24503 }
24504
24505 /*
24506 Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
24507 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
24508 full-duplex mode.
24509 */
24510 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24511 MA_WAVEFORMATEXTENSIBLE wf;
24512 MA_DSCBUFFERDESC descDS;
24513 ma_uint32 periodSizeInFrames;
24514 ma_uint32 periodCount;
24515 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
24516 MA_WAVEFORMATEXTENSIBLE* pActualFormat;
24517
24518 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);
24519 if (result != MA_SUCCESS) {
24520 return result;
24521 }
24522
24523 result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
24524 if (result != MA_SUCCESS) {
24525 ma_device_uninit__dsound(pDevice);
24526 return result;
24527 }
24528
24529 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec);
24530 if (result != MA_SUCCESS) {
24531 ma_device_uninit__dsound(pDevice);
24532 return result;
24533 }
24534
24535 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
24536 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
24537 wf.Samples.wValidBitsPerSample = wf.wBitsPerSample;
24538 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
24539
24540 /* The size of the buffer must be a clean multiple of the period count. */
24541 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile);
24542 periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;
24543
24544 MA_ZERO_OBJECT(&descDS);
24545 descDS.dwSize = sizeof(descDS);
24546 descDS.dwFlags = 0;
24547 descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign;
24548 descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf;
24549 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
24550 if (FAILED(hr)) {
24551 ma_device_uninit__dsound(pDevice);
24552 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
24553 return ma_result_from_HRESULT(hr);
24554 }
24555
24556 /* Get the _actual_ properties of the buffer. */
24557 pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
24558 hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
24559 if (FAILED(hr)) {
24560 ma_device_uninit__dsound(pDevice);
24561 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.");
24562 return ma_result_from_HRESULT(hr);
24563 }
24564
24565 /* We can now start setting the output data formats. */
24566 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
24567 pDescriptorCapture->channels = pActualFormat->nChannels;
24568 pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec;
24569
24570 /* Get the native channel map based on the channel mask. */
24571 if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
24572 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
24573 } else {
24574 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
24575 }
24576
24577 /*
24578 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
24579 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.
24580 */
24581 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {
24582 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;
24583 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
24584
24585 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, 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] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
24589 return ma_result_from_HRESULT(hr);
24590 }
24591 }
24592
24593 /* DirectSound should give us a buffer exactly the size we asked for. */
24594 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
24595 pDescriptorCapture->periodCount = periodCount;
24596 }
24597
24598 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24599 MA_WAVEFORMATEXTENSIBLE wf;
24600 MA_DSBUFFERDESC descDSPrimary;
24601 MA_DSCAPS caps;
24602 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
24603 MA_WAVEFORMATEXTENSIBLE* pActualFormat;
24604 ma_uint32 periodSizeInFrames;
24605 ma_uint32 periodCount;
24606 MA_DSBUFFERDESC descDS;
24607 WORD nativeChannelCount;
24608 DWORD nativeChannelMask = 0;
24609
24610 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);
24611 if (result != MA_SUCCESS) {
24612 return result;
24613 }
24614
24615 result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
24616 if (result != MA_SUCCESS) {
24617 ma_device_uninit__dsound(pDevice);
24618 return result;
24619 }
24620
24621 MA_ZERO_OBJECT(&descDSPrimary);
24622 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
24623 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
24624 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
24625 if (FAILED(hr)) {
24626 ma_device_uninit__dsound(pDevice);
24627 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.");
24628 return ma_result_from_HRESULT(hr);
24629 }
24630
24631
24632 /* We may want to make some adjustments to the format if we are using defaults. */
24633 MA_ZERO_OBJECT(&caps);
24634 caps.dwSize = sizeof(caps);
24635 hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
24636 if (FAILED(hr)) {
24637 ma_device_uninit__dsound(pDevice);
24638 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
24639 return ma_result_from_HRESULT(hr);
24640 }
24641
24642 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
24643 DWORD speakerConfig;
24644
24645 /* It supports at least stereo, but could support more. */
24646 nativeChannelCount = 2;
24647
24648 /* Look at the speaker configuration to get a better idea on the channel count. */
24649 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
24650 ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask);
24651 }
24652 } else {
24653 /* It does not support stereo, which means we are stuck with mono. */
24654 nativeChannelCount = 1;
24655 nativeChannelMask = 0x00000001;
24656 }
24657
24658 if (pDescriptorPlayback->channels == 0) {
24659 wf.nChannels = nativeChannelCount;
24660 wf.dwChannelMask = nativeChannelMask;
24661 }
24662
24663 if (pDescriptorPlayback->sampleRate == 0) {
24664 /* We base the sample rate on the values returned by GetCaps(). */
24665 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
24666 wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
24667 } else {
24668 wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
24669 }
24670 }
24671
24672 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
24673 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
24674
24675 /*
24676 From MSDN:
24677
24678 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
24679 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
24680 and compare the result with the format that was requested with the SetFormat method.
24681 */
24682 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
24683 if (FAILED(hr)) {
24684 /*
24685 If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have
24686 observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a
24687 sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will
24688 use 44100 for the sample rate.
24689 */
24690 wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */
24691 wf.wFormatTag = WAVE_FORMAT_PCM;
24692 wf.wBitsPerSample = 16;
24693 wf.nChannels = nativeChannelCount;
24694 wf.nSamplesPerSec = 44100;
24695 wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8);
24696 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
24697
24698 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
24699 if (FAILED(hr)) {
24700 ma_device_uninit__dsound(pDevice);
24701 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.");
24702 return ma_result_from_HRESULT(hr);
24703 }
24704 }
24705
24706 /* Get the _actual_ properties of the buffer. */
24707 pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
24708 hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
24709 if (FAILED(hr)) {
24710 ma_device_uninit__dsound(pDevice);
24711 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.");
24712 return ma_result_from_HRESULT(hr);
24713 }
24714
24715 /* We now have enough information to start setting some output properties. */
24716 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
24717 pDescriptorPlayback->channels = pActualFormat->nChannels;
24718 pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec;
24719
24720 /* Get the internal channel map based on the channel mask. */
24721 if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
24722 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
24723 } else {
24724 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
24725 }
24726
24727 /* The size of the buffer must be a clean multiple of the period count. */
24728 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
24729 periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;
24730
24731 /*
24732 Meaning of dwFlags (from MSDN):
24733
24734 DSBCAPS_CTRLPOSITIONNOTIFY
24735 The buffer has position notification capability.
24736
24737 DSBCAPS_GLOBALFOCUS
24738 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
24739 another application, even if the new application uses DirectSound.
24740
24741 DSBCAPS_GETCURRENTPOSITION2
24742 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
24743 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
24744 application can get a more accurate play cursor.
24745 */
24746 MA_ZERO_OBJECT(&descDS);
24747 descDS.dwSize = sizeof(descDS);
24748 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
24749 descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
24750 descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat;
24751 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
24752 if (FAILED(hr)) {
24753 ma_device_uninit__dsound(pDevice);
24754 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.");
24755 return ma_result_from_HRESULT(hr);
24756 }
24757
24758 /* DirectSound should give us a buffer exactly the size we asked for. */
24759 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
24760 pDescriptorPlayback->periodCount = periodCount;
24761 }
24762
24763 return MA_SUCCESS;
24764 }
24765
24766
24767 static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
24768 {
24769 ma_result result = MA_SUCCESS;
24770 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24771 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24772 HRESULT hr;
24773 DWORD lockOffsetInBytesCapture;
24774 DWORD lockSizeInBytesCapture;
24775 DWORD mappedSizeInBytesCapture;
24776 DWORD mappedDeviceFramesProcessedCapture;
24777 void* pMappedDeviceBufferCapture;
24778 DWORD lockOffsetInBytesPlayback;
24779 DWORD lockSizeInBytesPlayback;
24780 DWORD mappedSizeInBytesPlayback;
24781 void* pMappedDeviceBufferPlayback;
24782 DWORD prevReadCursorInBytesCapture = 0;
24783 DWORD prevPlayCursorInBytesPlayback = 0;
24784 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
24785 DWORD virtualWriteCursorInBytesPlayback = 0;
24786 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
24787 ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
24788 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
24789 ma_uint32 waitTimeInMilliseconds = 1;
24790
24791 MA_ASSERT(pDevice != NULL);
24792
24793 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
24794 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24795 hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING);
24796 if (FAILED(hr)) {
24797 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.");
24798 return ma_result_from_HRESULT(hr);
24799 }
24800 }
24801
24802 while (ma_device_get_state(pDevice) == ma_device_state_started) {
24803 switch (pDevice->type)
24804 {
24805 case ma_device_type_duplex:
24806 {
24807 DWORD physicalCaptureCursorInBytes;
24808 DWORD physicalReadCursorInBytes;
24809 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
24810 if (FAILED(hr)) {
24811 return ma_result_from_HRESULT(hr);
24812 }
24813
24814 /* If nothing is available we just sleep for a bit and return from this iteration. */
24815 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
24816 ma_sleep(waitTimeInMilliseconds);
24817 continue; /* Nothing is available in the capture buffer. */
24818 }
24819
24820 /*
24821 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
24822 we don't return until every frame has been copied over.
24823 */
24824 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
24825 /* The capture position has not looped. This is the simple case. */
24826 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
24827 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
24828 } else {
24829 /*
24830 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
24831 do it again from the start.
24832 */
24833 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
24834 /* Lock up to the end of the buffer. */
24835 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
24836 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
24837 } else {
24838 /* Lock starting from the start of the buffer. */
24839 lockOffsetInBytesCapture = 0;
24840 lockSizeInBytesCapture = physicalReadCursorInBytes;
24841 }
24842 }
24843
24844 if (lockSizeInBytesCapture == 0) {
24845 ma_sleep(waitTimeInMilliseconds);
24846 continue; /* Nothing is available in the capture buffer. */
24847 }
24848
24849 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
24850 if (FAILED(hr)) {
24851 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.");
24852 return ma_result_from_HRESULT(hr);
24853 }
24854
24855
24856 /* 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. */
24857 mappedDeviceFramesProcessedCapture = 0;
24858
24859 for (;;) { /* Keep writing to the playback device. */
24860 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24861 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
24862 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
24863 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
24864 ma_uint32 outputFramesInClientFormatCount;
24865 ma_uint32 outputFramesInClientFormatConsumed = 0;
24866 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
24867 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
24868 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
24869
24870 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
24871 if (result != MA_SUCCESS) {
24872 break;
24873 }
24874
24875 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
24876 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
24877
24878 ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
24879
24880 /* 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. */
24881 for (;;) {
24882 ma_uint32 framesWrittenThisIteration;
24883 DWORD physicalPlayCursorInBytes;
24884 DWORD physicalWriteCursorInBytes;
24885 DWORD availableBytesPlayback;
24886 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
24887
24888 /* We need the physical play and write cursors. */
24889 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
24890 break;
24891 }
24892
24893 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
24894 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
24895 }
24896 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
24897
24898 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
24899 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
24900 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
24901 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
24902 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
24903 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
24904 } else {
24905 /* This is an error. */
24906 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);
24907 availableBytesPlayback = 0;
24908 }
24909 } else {
24910 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
24911 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
24912 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
24913 } else {
24914 /* This is an error. */
24915 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);
24916 availableBytesPlayback = 0;
24917 }
24918 }
24919
24920 /* If there's no room available for writing we need to wait for more. */
24921 if (availableBytesPlayback == 0) {
24922 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
24923 if (!isPlaybackDeviceStarted) {
24924 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
24925 if (FAILED(hr)) {
24926 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
24927 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
24928 return ma_result_from_HRESULT(hr);
24929 }
24930 isPlaybackDeviceStarted = MA_TRUE;
24931 } else {
24932 ma_sleep(waitTimeInMilliseconds);
24933 continue;
24934 }
24935 }
24936
24937
24938 /* 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. */
24939 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
24940 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
24941 /* Same loop iteration. Go up to the end of the buffer. */
24942 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
24943 } else {
24944 /* Different loop iterations. Go up to the physical play cursor. */
24945 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
24946 }
24947
24948 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
24949 if (FAILED(hr)) {
24950 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.");
24951 result = ma_result_from_HRESULT(hr);
24952 break;
24953 }
24954
24955 /*
24956 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
24957 endless glitching due to it constantly running out of data.
24958 */
24959 if (isPlaybackDeviceStarted) {
24960 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
24961 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
24962 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
24963 if (silentPaddingInBytes > lockSizeInBytesPlayback) {
24964 silentPaddingInBytes = lockSizeInBytesPlayback;
24965 }
24966
24967 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);
24968 }
24969 }
24970
24971 /* At this point we have a buffer for output. */
24972 if (silentPaddingInBytes > 0) {
24973 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
24974 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
24975 } else {
24976 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
24977 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
24978 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
24979 void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
24980
24981 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
24982 if (result != MA_SUCCESS) {
24983 break;
24984 }
24985
24986 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
24987 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
24988 }
24989
24990
24991 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
24992 if (FAILED(hr)) {
24993 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.");
24994 result = ma_result_from_HRESULT(hr);
24995 break;
24996 }
24997
24998 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
24999 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
25000 virtualWriteCursorInBytesPlayback = 0;
25001 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
25002 }
25003
25004 /*
25005 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
25006 a bit of a buffer to prevent the playback buffer from getting starved.
25007 */
25008 framesWrittenToPlaybackDevice += framesWrittenThisIteration;
25009 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
25010 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
25011 if (FAILED(hr)) {
25012 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
25013 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
25014 return ma_result_from_HRESULT(hr);
25015 }
25016 isPlaybackDeviceStarted = MA_TRUE;
25017 }
25018
25019 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
25020 break; /* We're finished with the output data.*/
25021 }
25022 }
25023
25024 if (clientCapturedFramesToProcess == 0) {
25025 break; /* We just consumed every input sample. */
25026 }
25027 }
25028
25029
25030 /* At this point we're done with the mapped portion of the capture buffer. */
25031 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
25032 if (FAILED(hr)) {
25033 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.");
25034 return ma_result_from_HRESULT(hr);
25035 }
25036 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
25037 } break;
25038
25039
25040
25041 case ma_device_type_capture:
25042 {
25043 DWORD physicalCaptureCursorInBytes;
25044 DWORD physicalReadCursorInBytes;
25045 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
25046 if (FAILED(hr)) {
25047 return MA_ERROR;
25048 }
25049
25050 /* If the previous capture position is the same as the current position we need to wait a bit longer. */
25051 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
25052 ma_sleep(waitTimeInMilliseconds);
25053 continue;
25054 }
25055
25056 /* Getting here means we have capture data available. */
25057 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
25058 /* The capture position has not looped. This is the simple case. */
25059 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
25060 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
25061 } else {
25062 /*
25063 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
25064 do it again from the start.
25065 */
25066 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
25067 /* Lock up to the end of the buffer. */
25068 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
25069 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
25070 } else {
25071 /* Lock starting from the start of the buffer. */
25072 lockOffsetInBytesCapture = 0;
25073 lockSizeInBytesCapture = physicalReadCursorInBytes;
25074 }
25075 }
25076
25077 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
25078 ma_sleep(waitTimeInMilliseconds);
25079 continue; /* Nothing is available in the capture buffer. */
25080 }
25081
25082 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
25083 if (FAILED(hr)) {
25084 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.");
25085 result = ma_result_from_HRESULT(hr);
25086 }
25087
25088 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
25089 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
25090 }
25091
25092 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
25093
25094 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
25095 if (FAILED(hr)) {
25096 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.");
25097 return ma_result_from_HRESULT(hr);
25098 }
25099 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
25100
25101 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
25102 prevReadCursorInBytesCapture = 0;
25103 }
25104 } break;
25105
25106
25107
25108 case ma_device_type_playback:
25109 {
25110 DWORD availableBytesPlayback;
25111 DWORD physicalPlayCursorInBytes;
25112 DWORD physicalWriteCursorInBytes;
25113 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
25114 if (FAILED(hr)) {
25115 break;
25116 }
25117
25118 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
25119 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
25120 }
25121 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
25122
25123 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
25124 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
25125 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
25126 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
25127 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
25128 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
25129 } else {
25130 /* This is an error. */
25131 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);
25132 availableBytesPlayback = 0;
25133 }
25134 } else {
25135 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
25136 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
25137 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
25138 } else {
25139 /* This is an error. */
25140 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);
25141 availableBytesPlayback = 0;
25142 }
25143 }
25144
25145 /* If there's no room available for writing we need to wait for more. */
25146 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
25147 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
25148 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
25149 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
25150 if (FAILED(hr)) {
25151 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
25152 return ma_result_from_HRESULT(hr);
25153 }
25154 isPlaybackDeviceStarted = MA_TRUE;
25155 } else {
25156 ma_sleep(waitTimeInMilliseconds);
25157 continue;
25158 }
25159 }
25160
25161 /* 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. */
25162 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
25163 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
25164 /* Same loop iteration. Go up to the end of the buffer. */
25165 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
25166 } else {
25167 /* Different loop iterations. Go up to the physical play cursor. */
25168 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
25169 }
25170
25171 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
25172 if (FAILED(hr)) {
25173 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.");
25174 result = ma_result_from_HRESULT(hr);
25175 break;
25176 }
25177
25178 /* At this point we have a buffer for output. */
25179 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
25180
25181 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
25182 if (FAILED(hr)) {
25183 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.");
25184 result = ma_result_from_HRESULT(hr);
25185 break;
25186 }
25187
25188 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
25189 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
25190 virtualWriteCursorInBytesPlayback = 0;
25191 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
25192 }
25193
25194 /*
25195 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
25196 a bit of a buffer to prevent the playback buffer from getting starved.
25197 */
25198 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
25199 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
25200 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
25201 if (FAILED(hr)) {
25202 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
25203 return ma_result_from_HRESULT(hr);
25204 }
25205 isPlaybackDeviceStarted = MA_TRUE;
25206 }
25207 } break;
25208
25209
25210 default: return MA_INVALID_ARGS; /* Invalid device type. */
25211 }
25212
25213 if (result != MA_SUCCESS) {
25214 return result;
25215 }
25216 }
25217
25218 /* Getting here means the device is being stopped. */
25219 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25220 hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
25221 if (FAILED(hr)) {
25222 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.");
25223 return ma_result_from_HRESULT(hr);
25224 }
25225 }
25226
25227 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25228 /* 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. */
25229 if (isPlaybackDeviceStarted) {
25230 for (;;) {
25231 DWORD availableBytesPlayback = 0;
25232 DWORD physicalPlayCursorInBytes;
25233 DWORD physicalWriteCursorInBytes;
25234 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
25235 if (FAILED(hr)) {
25236 break;
25237 }
25238
25239 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
25240 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
25241 }
25242 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
25243
25244 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
25245 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
25246 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
25247 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
25248 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
25249 } else {
25250 break;
25251 }
25252 } else {
25253 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
25254 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
25255 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
25256 } else {
25257 break;
25258 }
25259 }
25260
25261 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
25262 break;
25263 }
25264
25265 ma_sleep(waitTimeInMilliseconds);
25266 }
25267 }
25268
25269 hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
25270 if (FAILED(hr)) {
25271 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.");
25272 return ma_result_from_HRESULT(hr);
25273 }
25274
25275 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
25276 }
25277
25278 return MA_SUCCESS;
25279 }
25280
25281 static ma_result ma_context_uninit__dsound(ma_context* pContext)
25282 {
25283 MA_ASSERT(pContext != NULL);
25284 MA_ASSERT(pContext->backend == ma_backend_dsound);
25285
25286 ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
25287
25288 return MA_SUCCESS;
25289 }
25290
25291 static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
25292 {
25293 MA_ASSERT(pContext != NULL);
25294
25295 (void)pConfig;
25296
25297 pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
25298 if (pContext->dsound.hDSoundDLL == NULL) {
25299 return MA_API_NOT_FOUND;
25300 }
25301
25302 pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
25303 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
25304 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
25305 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
25306
25307 /*
25308 We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too
25309 well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient
25310 place to just disable the DirectSound backend for Windows 95.
25311 */
25312 if (pContext->dsound.DirectSoundCreate == NULL ||
25313 pContext->dsound.DirectSoundEnumerateA == NULL ||
25314 pContext->dsound.DirectSoundCaptureCreate == NULL ||
25315 pContext->dsound.DirectSoundCaptureEnumerateA == NULL) {
25316 return MA_API_NOT_FOUND;
25317 }
25318
25319 pCallbacks->onContextInit = ma_context_init__dsound;
25320 pCallbacks->onContextUninit = ma_context_uninit__dsound;
25321 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
25322 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound;
25323 pCallbacks->onDeviceInit = ma_device_init__dsound;
25324 pCallbacks->onDeviceUninit = ma_device_uninit__dsound;
25325 pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */
25326 pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */
25327 pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */
25328 pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */
25329 pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound;
25330
25331 return MA_SUCCESS;
25332 }
25333 #endif
25334
25335
25336
25337 /******************************************************************************
25338
25339 WinMM Backend
25340
25341 ******************************************************************************/
25342 #ifdef MA_HAS_WINMM
25343
25344 /*
25345 Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN
25346 is defined. We need to define the types and functions we need manually.
25347 */
25348 #define MA_MMSYSERR_NOERROR 0
25349 #define MA_MMSYSERR_ERROR 1
25350 #define MA_MMSYSERR_BADDEVICEID 2
25351 #define MA_MMSYSERR_INVALHANDLE 5
25352 #define MA_MMSYSERR_NOMEM 7
25353 #define MA_MMSYSERR_INVALFLAG 10
25354 #define MA_MMSYSERR_INVALPARAM 11
25355 #define MA_MMSYSERR_HANDLEBUSY 12
25356
25357 #define MA_CALLBACK_EVENT 0x00050000
25358 #define MA_WAVE_ALLOWSYNC 0x0002
25359
25360 #define MA_WHDR_DONE 0x00000001
25361 #define MA_WHDR_PREPARED 0x00000002
25362 #define MA_WHDR_BEGINLOOP 0x00000004
25363 #define MA_WHDR_ENDLOOP 0x00000008
25364 #define MA_WHDR_INQUEUE 0x00000010
25365
25366 #define MA_MAXPNAMELEN 32
25367
25368 typedef void* MA_HWAVEIN;
25369 typedef void* MA_HWAVEOUT;
25370 typedef UINT MA_MMRESULT;
25371 typedef UINT MA_MMVERSION;
25372
25373 typedef struct
25374 {
25375 WORD wMid;
25376 WORD wPid;
25377 MA_MMVERSION vDriverVersion;
25378 CHAR szPname[MA_MAXPNAMELEN];
25379 DWORD dwFormats;
25380 WORD wChannels;
25381 WORD wReserved1;
25382 } MA_WAVEINCAPSA;
25383
25384 typedef struct
25385 {
25386 WORD wMid;
25387 WORD wPid;
25388 MA_MMVERSION vDriverVersion;
25389 CHAR szPname[MA_MAXPNAMELEN];
25390 DWORD dwFormats;
25391 WORD wChannels;
25392 WORD wReserved1;
25393 DWORD dwSupport;
25394 } MA_WAVEOUTCAPSA;
25395
25396 typedef struct tagWAVEHDR
25397 {
25398 char* lpData;
25399 DWORD dwBufferLength;
25400 DWORD dwBytesRecorded;
25401 DWORD_PTR dwUser;
25402 DWORD dwFlags;
25403 DWORD dwLoops;
25404 struct tagWAVEHDR* lpNext;
25405 DWORD_PTR reserved;
25406 } MA_WAVEHDR;
25407
25408 typedef struct
25409 {
25410 WORD wMid;
25411 WORD wPid;
25412 MA_MMVERSION vDriverVersion;
25413 CHAR szPname[MA_MAXPNAMELEN];
25414 DWORD dwFormats;
25415 WORD wChannels;
25416 WORD wReserved1;
25417 DWORD dwSupport;
25418 GUID ManufacturerGuid;
25419 GUID ProductGuid;
25420 GUID NameGuid;
25421 } MA_WAVEOUTCAPS2A;
25422
25423 typedef struct
25424 {
25425 WORD wMid;
25426 WORD wPid;
25427 MA_MMVERSION vDriverVersion;
25428 CHAR szPname[MA_MAXPNAMELEN];
25429 DWORD dwFormats;
25430 WORD wChannels;
25431 WORD wReserved1;
25432 GUID ManufacturerGuid;
25433 GUID ProductGuid;
25434 GUID NameGuid;
25435 } MA_WAVEINCAPS2A;
25436
25437 typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
25438 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc);
25439 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
25440 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo);
25441 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
25442 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
25443 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
25444 typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo);
25445 typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
25446 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic);
25447 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
25448 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi);
25449 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
25450 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
25451 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
25452 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi);
25453 typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi);
25454
25455 static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM)
25456 {
25457 switch (resultMM)
25458 {
25459 case MA_MMSYSERR_NOERROR: return MA_SUCCESS;
25460 case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
25461 case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
25462 case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
25463 case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
25464 case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
25465 case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY;
25466 case MA_MMSYSERR_ERROR: return MA_ERROR;
25467 default: return MA_ERROR;
25468 }
25469 }
25470
25471 static char* ma_find_last_character(char* str, char ch)
25472 {
25473 char* last;
25474
25475 if (str == NULL) {
25476 return NULL;
25477 }
25478
25479 last = NULL;
25480 while (*str != '\0') {
25481 if (*str == ch) {
25482 last = str;
25483 }
25484
25485 str += 1;
25486 }
25487
25488 return last;
25489 }
25490
25491 static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
25492 {
25493 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
25494 }
25495
25496
25497 /*
25498 Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
25499 we can do things generically and typesafely. Names are being kept the same for consistency.
25500 */
25501 typedef struct
25502 {
25503 CHAR szPname[MA_MAXPNAMELEN];
25504 DWORD dwFormats;
25505 WORD wChannels;
25506 GUID NameGuid;
25507 } MA_WAVECAPSA;
25508
25509 static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
25510 {
25511 WORD bitsPerSample = 0;
25512 DWORD sampleRate = 0;
25513
25514 if (pBitsPerSample) {
25515 *pBitsPerSample = 0;
25516 }
25517 if (pSampleRate) {
25518 *pSampleRate = 0;
25519 }
25520
25521 if (channels == 1) {
25522 bitsPerSample = 16;
25523 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
25524 sampleRate = 48000;
25525 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
25526 sampleRate = 44100;
25527 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
25528 sampleRate = 22050;
25529 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
25530 sampleRate = 11025;
25531 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
25532 sampleRate = 96000;
25533 } else {
25534 bitsPerSample = 8;
25535 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
25536 sampleRate = 48000;
25537 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
25538 sampleRate = 44100;
25539 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
25540 sampleRate = 22050;
25541 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
25542 sampleRate = 11025;
25543 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
25544 sampleRate = 96000;
25545 } else {
25546 return MA_FORMAT_NOT_SUPPORTED;
25547 }
25548 }
25549 } else {
25550 bitsPerSample = 16;
25551 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
25552 sampleRate = 48000;
25553 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
25554 sampleRate = 44100;
25555 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
25556 sampleRate = 22050;
25557 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
25558 sampleRate = 11025;
25559 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
25560 sampleRate = 96000;
25561 } else {
25562 bitsPerSample = 8;
25563 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
25564 sampleRate = 48000;
25565 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
25566 sampleRate = 44100;
25567 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
25568 sampleRate = 22050;
25569 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
25570 sampleRate = 11025;
25571 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
25572 sampleRate = 96000;
25573 } else {
25574 return MA_FORMAT_NOT_SUPPORTED;
25575 }
25576 }
25577 }
25578
25579 if (pBitsPerSample) {
25580 *pBitsPerSample = bitsPerSample;
25581 }
25582 if (pSampleRate) {
25583 *pSampleRate = sampleRate;
25584 }
25585
25586 return MA_SUCCESS;
25587 }
25588
25589 static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF)
25590 {
25591 ma_result result;
25592
25593 MA_ASSERT(pWF != NULL);
25594
25595 MA_ZERO_OBJECT(pWF);
25596 pWF->cbSize = sizeof(*pWF);
25597 pWF->wFormatTag = WAVE_FORMAT_PCM;
25598 pWF->nChannels = (WORD)channels;
25599 if (pWF->nChannels > 2) {
25600 pWF->nChannels = 2;
25601 }
25602
25603 result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);
25604 if (result != MA_SUCCESS) {
25605 return result;
25606 }
25607
25608 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
25609 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
25610
25611 return MA_SUCCESS;
25612 }
25613
25614 static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
25615 {
25616 WORD bitsPerSample;
25617 DWORD sampleRate;
25618 ma_result result;
25619
25620 MA_ASSERT(pContext != NULL);
25621 MA_ASSERT(pCaps != NULL);
25622 MA_ASSERT(pDeviceInfo != NULL);
25623
25624 /*
25625 Name / Description
25626
25627 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
25628 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
25629 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.
25630 */
25631
25632 /* Set the default to begin with. */
25633 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
25634
25635 /*
25636 Now try the registry. There's a few things to consider here:
25637 - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
25638 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
25639 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
25640 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
25641 but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
25642 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
25643 name, and then concatenate the name from the registry.
25644 */
25645 if (!ma_is_guid_null(&pCaps->NameGuid)) {
25646 WCHAR guidStrW[256];
25647 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
25648 char guidStr[256];
25649 char keyStr[1024];
25650 HKEY hKey;
25651
25652 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
25653
25654 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
25655 ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
25656
25657 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
25658 BYTE nameFromReg[512];
25659 DWORD nameFromRegSize = sizeof(nameFromReg);
25660 LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize);
25661 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
25662
25663 if (resultWin32 == ERROR_SUCCESS) {
25664 /* We have the value from the registry, so now we need to construct the name string. */
25665 char name[1024];
25666 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
25667 char* nameBeg = ma_find_last_character(name, '(');
25668 if (nameBeg != NULL) {
25669 size_t leadingLen = (nameBeg - name);
25670 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
25671
25672 /* The closing ")", if it can fit. */
25673 if (leadingLen + nameFromRegSize < sizeof(name)-1) {
25674 ma_strcat_s(name, sizeof(name), ")");
25675 }
25676
25677 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
25678 }
25679 }
25680 }
25681 }
25682 }
25683 }
25684
25685
25686 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
25687 if (result != MA_SUCCESS) {
25688 return result;
25689 }
25690
25691 if (bitsPerSample == 8) {
25692 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
25693 } else if (bitsPerSample == 16) {
25694 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
25695 } else if (bitsPerSample == 24) {
25696 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
25697 } else if (bitsPerSample == 32) {
25698 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
25699 } else {
25700 return MA_FORMAT_NOT_SUPPORTED;
25701 }
25702 pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels;
25703 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
25704 pDeviceInfo->nativeDataFormats[0].flags = 0;
25705 pDeviceInfo->nativeDataFormatCount = 1;
25706
25707 return MA_SUCCESS;
25708 }
25709
25710 static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
25711 {
25712 MA_WAVECAPSA caps;
25713
25714 MA_ASSERT(pContext != NULL);
25715 MA_ASSERT(pCaps != NULL);
25716 MA_ASSERT(pDeviceInfo != NULL);
25717
25718 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
25719 caps.dwFormats = pCaps->dwFormats;
25720 caps.wChannels = pCaps->wChannels;
25721 caps.NameGuid = pCaps->NameGuid;
25722 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
25723 }
25724
25725 static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
25726 {
25727 MA_WAVECAPSA caps;
25728
25729 MA_ASSERT(pContext != NULL);
25730 MA_ASSERT(pCaps != NULL);
25731 MA_ASSERT(pDeviceInfo != NULL);
25732
25733 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
25734 caps.dwFormats = pCaps->dwFormats;
25735 caps.wChannels = pCaps->wChannels;
25736 caps.NameGuid = pCaps->NameGuid;
25737 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
25738 }
25739
25740
25741 static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25742 {
25743 UINT playbackDeviceCount;
25744 UINT captureDeviceCount;
25745 UINT iPlaybackDevice;
25746 UINT iCaptureDevice;
25747
25748 MA_ASSERT(pContext != NULL);
25749 MA_ASSERT(callback != NULL);
25750
25751 /* Playback. */
25752 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
25753 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
25754 MA_MMRESULT result;
25755 MA_WAVEOUTCAPS2A caps;
25756
25757 MA_ZERO_OBJECT(&caps);
25758
25759 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
25760 if (result == MA_MMSYSERR_NOERROR) {
25761 ma_device_info deviceInfo;
25762
25763 MA_ZERO_OBJECT(&deviceInfo);
25764 deviceInfo.id.winmm = iPlaybackDevice;
25765
25766 /* The first enumerated device is the default device. */
25767 if (iPlaybackDevice == 0) {
25768 deviceInfo.isDefault = MA_TRUE;
25769 }
25770
25771 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
25772 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
25773 if (cbResult == MA_FALSE) {
25774 return MA_SUCCESS; /* Enumeration was stopped. */
25775 }
25776 }
25777 }
25778 }
25779
25780 /* Capture. */
25781 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
25782 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
25783 MA_MMRESULT result;
25784 MA_WAVEINCAPS2A caps;
25785
25786 MA_ZERO_OBJECT(&caps);
25787
25788 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
25789 if (result == MA_MMSYSERR_NOERROR) {
25790 ma_device_info deviceInfo;
25791
25792 MA_ZERO_OBJECT(&deviceInfo);
25793 deviceInfo.id.winmm = iCaptureDevice;
25794
25795 /* The first enumerated device is the default device. */
25796 if (iCaptureDevice == 0) {
25797 deviceInfo.isDefault = MA_TRUE;
25798 }
25799
25800 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
25801 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
25802 if (cbResult == MA_FALSE) {
25803 return MA_SUCCESS; /* Enumeration was stopped. */
25804 }
25805 }
25806 }
25807 }
25808
25809 return MA_SUCCESS;
25810 }
25811
25812 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)
25813 {
25814 UINT winMMDeviceID;
25815
25816 MA_ASSERT(pContext != NULL);
25817
25818 winMMDeviceID = 0;
25819 if (pDeviceID != NULL) {
25820 winMMDeviceID = (UINT)pDeviceID->winmm;
25821 }
25822
25823 pDeviceInfo->id.winmm = winMMDeviceID;
25824
25825 /* The first ID is the default device. */
25826 if (winMMDeviceID == 0) {
25827 pDeviceInfo->isDefault = MA_TRUE;
25828 }
25829
25830 if (deviceType == ma_device_type_playback) {
25831 MA_MMRESULT result;
25832 MA_WAVEOUTCAPS2A caps;
25833
25834 MA_ZERO_OBJECT(&caps);
25835
25836 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
25837 if (result == MA_MMSYSERR_NOERROR) {
25838 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
25839 }
25840 } else {
25841 MA_MMRESULT result;
25842 MA_WAVEINCAPS2A caps;
25843
25844 MA_ZERO_OBJECT(&caps);
25845
25846 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
25847 if (result == MA_MMSYSERR_NOERROR) {
25848 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
25849 }
25850 }
25851
25852 return MA_NO_DEVICE;
25853 }
25854
25855
25856 static ma_result ma_device_uninit__winmm(ma_device* pDevice)
25857 {
25858 MA_ASSERT(pDevice != NULL);
25859
25860 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25861 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
25862 CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
25863 }
25864
25865 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25866 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
25867 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
25868 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
25869 }
25870
25871 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
25872
25873 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
25874
25875 return MA_SUCCESS;
25876 }
25877
25878 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
25879 {
25880 /* WinMM has a minimum period size of 40ms. */
25881 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);
25882 ma_uint32 periodSizeInFrames;
25883
25884 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
25885 if (periodSizeInFrames < minPeriodSizeInFrames) {
25886 periodSizeInFrames = minPeriodSizeInFrames;
25887 }
25888
25889 return periodSizeInFrames;
25890 }
25891
25892 static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
25893 {
25894 const char* errorMsg = "";
25895 ma_result errorCode = MA_ERROR;
25896 ma_result result = MA_SUCCESS;
25897 ma_uint32 heapSize;
25898 UINT winMMDeviceIDPlayback = 0;
25899 UINT winMMDeviceIDCapture = 0;
25900
25901 MA_ASSERT(pDevice != NULL);
25902
25903 MA_ZERO_OBJECT(&pDevice->winmm);
25904
25905 if (pConfig->deviceType == ma_device_type_loopback) {
25906 return MA_DEVICE_TYPE_NOT_SUPPORTED;
25907 }
25908
25909 /* No exlusive mode with WinMM. */
25910 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
25911 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
25912 return MA_SHARE_MODE_NOT_SUPPORTED;
25913 }
25914
25915 if (pDescriptorPlayback->pDeviceID != NULL) {
25916 winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;
25917 }
25918 if (pDescriptorCapture->pDeviceID != NULL) {
25919 winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;
25920 }
25921
25922 /* The capture device needs to be initialized first. */
25923 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
25924 MA_WAVEINCAPSA caps;
25925 MA_WAVEFORMATEX wf;
25926 MA_MMRESULT resultMM;
25927
25928 /* We use an event to know when a new fragment needs to be enqueued. */
25929 pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
25930 if (pDevice->winmm.hEventCapture == NULL) {
25931 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
25932 goto on_error;
25933 }
25934
25935 /* The format should be based on the device's actual format. */
25936 if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
25937 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
25938 goto on_error;
25939 }
25940
25941 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
25942 if (result != MA_SUCCESS) {
25943 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
25944 goto on_error;
25945 }
25946
25947 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);
25948 if (resultMM != MA_MMSYSERR_NOERROR) {
25949 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
25950 goto on_error;
25951 }
25952
25953 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf);
25954 pDescriptorCapture->channels = wf.nChannels;
25955 pDescriptorCapture->sampleRate = wf.nSamplesPerSec;
25956 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
25957 pDescriptorCapture->periodCount = pDescriptorCapture->periodCount;
25958 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
25959 }
25960
25961 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
25962 MA_WAVEOUTCAPSA caps;
25963 MA_WAVEFORMATEX wf;
25964 MA_MMRESULT resultMM;
25965
25966 /* We use an event to know when a new fragment needs to be enqueued. */
25967 pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
25968 if (pDevice->winmm.hEventPlayback == NULL) {
25969 errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
25970 goto on_error;
25971 }
25972
25973 /* The format should be based on the device's actual format. */
25974 if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
25975 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
25976 goto on_error;
25977 }
25978
25979 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
25980 if (result != MA_SUCCESS) {
25981 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
25982 goto on_error;
25983 }
25984
25985 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);
25986 if (resultMM != MA_MMSYSERR_NOERROR) {
25987 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
25988 goto on_error;
25989 }
25990
25991 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf);
25992 pDescriptorPlayback->channels = wf.nChannels;
25993 pDescriptorPlayback->sampleRate = wf.nSamplesPerSec;
25994 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
25995 pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount;
25996 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
25997 }
25998
25999 /*
26000 The heap allocated data is allocated like so:
26001
26002 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
26003 */
26004 heapSize = 0;
26005 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26006 heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
26007 }
26008 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26009 heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));
26010 }
26011
26012 pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks);
26013 if (pDevice->winmm._pHeapData == NULL) {
26014 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
26015 goto on_error;
26016 }
26017
26018 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
26019
26020 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26021 ma_uint32 iPeriod;
26022
26023 if (pConfig->deviceType == ma_device_type_capture) {
26024 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
26025 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
26026 } else {
26027 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
26028 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));
26029 }
26030
26031 /* Prepare headers. */
26032 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
26033 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);
26034
26035 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
26036 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
26037 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
26038 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
26039 ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
26040
26041 /*
26042 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
26043 it's unlocked and available for writing. A value of 1 means it's locked.
26044 */
26045 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
26046 }
26047 }
26048
26049 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26050 ma_uint32 iPeriod;
26051
26052 if (pConfig->deviceType == ma_device_type_playback) {
26053 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
26054 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount);
26055 } else {
26056 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
26057 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));
26058 }
26059
26060 /* Prepare headers. */
26061 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
26062 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);
26063
26064 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
26065 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
26066 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
26067 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
26068 ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
26069
26070 /*
26071 The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means
26072 it's unlocked and available for writing. A value of 1 means it's locked.
26073 */
26074 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
26075 }
26076 }
26077
26078 return MA_SUCCESS;
26079
26080 on_error:
26081 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26082 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
26083 ma_uint32 iPeriod;
26084 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
26085 ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
26086 }
26087 }
26088
26089 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
26090 }
26091
26092 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26093 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
26094 ma_uint32 iPeriod;
26095 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
26096 ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
26097 }
26098 }
26099
26100 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
26101 }
26102
26103 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
26104
26105 if (errorMsg != NULL && errorMsg[0] != '\0') {
26106 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg);
26107 }
26108
26109 return errorCode;
26110 }
26111
26112 static ma_result ma_device_start__winmm(ma_device* pDevice)
26113 {
26114 MA_ASSERT(pDevice != NULL);
26115
26116 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26117 MA_MMRESULT resultMM;
26118 MA_WAVEHDR* pWAVEHDR;
26119 ma_uint32 iPeriod;
26120
26121 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
26122
26123 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
26124 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
26125
26126 /* 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. */
26127 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
26128 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
26129 if (resultMM != MA_MMSYSERR_NOERROR) {
26130 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.");
26131 return ma_result_from_MMRESULT(resultMM);
26132 }
26133
26134 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
26135 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
26136 }
26137
26138 /* Capture devices need to be explicitly started, unlike playback devices. */
26139 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
26140 if (resultMM != MA_MMSYSERR_NOERROR) {
26141 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.");
26142 return ma_result_from_MMRESULT(resultMM);
26143 }
26144 }
26145
26146 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26147 /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */
26148 }
26149
26150 return MA_SUCCESS;
26151 }
26152
26153 static ma_result ma_device_stop__winmm(ma_device* pDevice)
26154 {
26155 MA_MMRESULT resultMM;
26156
26157 MA_ASSERT(pDevice != NULL);
26158
26159 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26160 if (pDevice->winmm.hDeviceCapture == NULL) {
26161 return MA_INVALID_ARGS;
26162 }
26163
26164 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
26165 if (resultMM != MA_MMSYSERR_NOERROR) {
26166 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device.");
26167 }
26168 }
26169
26170 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26171 ma_uint32 iPeriod;
26172 MA_WAVEHDR* pWAVEHDR;
26173
26174 if (pDevice->winmm.hDevicePlayback == NULL) {
26175 return MA_INVALID_ARGS;
26176 }
26177
26178 /* 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. */
26179 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
26180 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
26181 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
26182 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
26183 break; /* An error occurred so just abandon ship and stop the device without draining. */
26184 }
26185
26186 pWAVEHDR[iPeriod].dwUser = 0;
26187 }
26188 }
26189
26190 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
26191 if (resultMM != MA_MMSYSERR_NOERROR) {
26192 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device.");
26193 }
26194 }
26195
26196 return MA_SUCCESS;
26197 }
26198
26199 static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
26200 {
26201 ma_result result = MA_SUCCESS;
26202 MA_MMRESULT resultMM;
26203 ma_uint32 totalFramesWritten;
26204 MA_WAVEHDR* pWAVEHDR;
26205
26206 MA_ASSERT(pDevice != NULL);
26207 MA_ASSERT(pPCMFrames != NULL);
26208
26209 if (pFramesWritten != NULL) {
26210 *pFramesWritten = 0;
26211 }
26212
26213 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
26214
26215 /* Keep processing as much data as possible. */
26216 totalFramesWritten = 0;
26217 while (totalFramesWritten < frameCount) {
26218 /* If the current header has some space available we need to write part of it. */
26219 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
26220 /*
26221 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
26222 write it out and move on to the next iteration.
26223 */
26224 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26225 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
26226
26227 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
26228 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
26229 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
26230 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
26231
26232 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
26233 totalFramesWritten += framesToCopy;
26234
26235 /* If we've consumed the buffer entirely we need to write it out to the device. */
26236 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
26237 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
26238 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
26239
26240 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
26241 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
26242
26243 /* The device will be started here. */
26244 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR));
26245 if (resultMM != MA_MMSYSERR_NOERROR) {
26246 result = ma_result_from_MMRESULT(resultMM);
26247 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.");
26248 break;
26249 }
26250
26251 /* Make sure we move to the next header. */
26252 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
26253 pDevice->winmm.headerFramesConsumedPlayback = 0;
26254 }
26255
26256 /* If at this point we have consumed the entire input buffer we can return. */
26257 MA_ASSERT(totalFramesWritten <= frameCount);
26258 if (totalFramesWritten == frameCount) {
26259 break;
26260 }
26261
26262 /* Getting here means there's more to process. */
26263 continue;
26264 }
26265
26266 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
26267 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
26268 result = MA_ERROR;
26269 break;
26270 }
26271
26272 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
26273 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) {
26274 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
26275 pDevice->winmm.headerFramesConsumedPlayback = 0;
26276 }
26277
26278 /* If the device has been stopped we need to break. */
26279 if (ma_device_get_state(pDevice) != ma_device_state_started) {
26280 break;
26281 }
26282 }
26283
26284 if (pFramesWritten != NULL) {
26285 *pFramesWritten = totalFramesWritten;
26286 }
26287
26288 return result;
26289 }
26290
26291 static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
26292 {
26293 ma_result result = MA_SUCCESS;
26294 MA_MMRESULT resultMM;
26295 ma_uint32 totalFramesRead;
26296 MA_WAVEHDR* pWAVEHDR;
26297
26298 MA_ASSERT(pDevice != NULL);
26299 MA_ASSERT(pPCMFrames != NULL);
26300
26301 if (pFramesRead != NULL) {
26302 *pFramesRead = 0;
26303 }
26304
26305 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
26306
26307 /* Keep processing as much data as possible. */
26308 totalFramesRead = 0;
26309 while (totalFramesRead < frameCount) {
26310 /* If the current header has some space available we need to write part of it. */
26311 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
26312 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
26313 ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26314 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
26315
26316 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
26317 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
26318 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
26319 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
26320
26321 pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
26322 totalFramesRead += framesToCopy;
26323
26324 /* If we've consumed the buffer entirely we need to add it back to the device. */
26325 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
26326 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
26327 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
26328
26329 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
26330 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
26331
26332 /* The device will be started here. */
26333 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR));
26334 if (resultMM != MA_MMSYSERR_NOERROR) {
26335 result = ma_result_from_MMRESULT(resultMM);
26336 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.");
26337 break;
26338 }
26339
26340 /* Make sure we move to the next header. */
26341 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
26342 pDevice->winmm.headerFramesConsumedCapture = 0;
26343 }
26344
26345 /* If at this point we have filled the entire input buffer we can return. */
26346 MA_ASSERT(totalFramesRead <= frameCount);
26347 if (totalFramesRead == frameCount) {
26348 break;
26349 }
26350
26351 /* Getting here means there's more to process. */
26352 continue;
26353 }
26354
26355 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
26356 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
26357 result = MA_ERROR;
26358 break;
26359 }
26360
26361 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
26362 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) {
26363 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
26364 pDevice->winmm.headerFramesConsumedCapture = 0;
26365 }
26366
26367 /* If the device has been stopped we need to break. */
26368 if (ma_device_get_state(pDevice) != ma_device_state_started) {
26369 break;
26370 }
26371 }
26372
26373 if (pFramesRead != NULL) {
26374 *pFramesRead = totalFramesRead;
26375 }
26376
26377 return result;
26378 }
26379
26380 static ma_result ma_context_uninit__winmm(ma_context* pContext)
26381 {
26382 MA_ASSERT(pContext != NULL);
26383 MA_ASSERT(pContext->backend == ma_backend_winmm);
26384
26385 ma_dlclose(pContext, pContext->winmm.hWinMM);
26386 return MA_SUCCESS;
26387 }
26388
26389 static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
26390 {
26391 MA_ASSERT(pContext != NULL);
26392
26393 (void)pConfig;
26394
26395 pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
26396 if (pContext->winmm.hWinMM == NULL) {
26397 return MA_NO_BACKEND;
26398 }
26399
26400 pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
26401 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
26402 pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
26403 pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
26404 pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
26405 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
26406 pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
26407 pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
26408 pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
26409 pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
26410 pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
26411 pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
26412 pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
26413 pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
26414 pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
26415 pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
26416 pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
26417
26418 pCallbacks->onContextInit = ma_context_init__winmm;
26419 pCallbacks->onContextUninit = ma_context_uninit__winmm;
26420 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
26421 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
26422 pCallbacks->onDeviceInit = ma_device_init__winmm;
26423 pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
26424 pCallbacks->onDeviceStart = ma_device_start__winmm;
26425 pCallbacks->onDeviceStop = ma_device_stop__winmm;
26426 pCallbacks->onDeviceRead = ma_device_read__winmm;
26427 pCallbacks->onDeviceWrite = ma_device_write__winmm;
26428 pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */
26429
26430 return MA_SUCCESS;
26431 }
26432 #endif
26433
26434
26435
26436
26437 /******************************************************************************
26438
26439 ALSA Backend
26440
26441 ******************************************************************************/
26442 #ifdef MA_HAS_ALSA
26443
26444 #include <poll.h> /* poll(), struct pollfd */
26445 #include <sys/eventfd.h> /* eventfd() */
26446
26447 #ifdef MA_NO_RUNTIME_LINKING
26448
26449 /* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
26450 #if !defined(__cplusplus)
26451 #if defined(__STRICT_ANSI__)
26452 #if !defined(inline)
26453 #define inline __inline__ __attribute__((always_inline))
26454 #define MA_INLINE_DEFINED
26455 #endif
26456 #endif
26457 #endif
26458 #include <alsa/asoundlib.h>
26459 #if defined(MA_INLINE_DEFINED)
26460 #undef inline
26461 #undef MA_INLINE_DEFINED
26462 #endif
26463
26464 typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
26465 typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
26466 typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
26467 typedef snd_pcm_format_t ma_snd_pcm_format_t;
26468 typedef snd_pcm_access_t ma_snd_pcm_access_t;
26469 typedef snd_pcm_t ma_snd_pcm_t;
26470 typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
26471 typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
26472 typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
26473 typedef snd_pcm_info_t ma_snd_pcm_info_t;
26474 typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
26475 typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
26476 typedef snd_pcm_state_t ma_snd_pcm_state_t;
26477
26478 /* snd_pcm_stream_t */
26479 #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
26480 #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
26481
26482 /* snd_pcm_format_t */
26483 #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
26484 #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
26485 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
26486 #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
26487 #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
26488 #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
26489 #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
26490 #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
26491 #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
26492 #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
26493 #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
26494 #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
26495 #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
26496 #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
26497 #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
26498 #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
26499
26500 /* ma_snd_pcm_access_t */
26501 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
26502 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
26503 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
26504 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
26505 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
26506
26507 /* Channel positions. */
26508 #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
26509 #define MA_SND_CHMAP_NA SND_CHMAP_NA
26510 #define MA_SND_CHMAP_MONO SND_CHMAP_MONO
26511 #define MA_SND_CHMAP_FL SND_CHMAP_FL
26512 #define MA_SND_CHMAP_FR SND_CHMAP_FR
26513 #define MA_SND_CHMAP_RL SND_CHMAP_RL
26514 #define MA_SND_CHMAP_RR SND_CHMAP_RR
26515 #define MA_SND_CHMAP_FC SND_CHMAP_FC
26516 #define MA_SND_CHMAP_LFE SND_CHMAP_LFE
26517 #define MA_SND_CHMAP_SL SND_CHMAP_SL
26518 #define MA_SND_CHMAP_SR SND_CHMAP_SR
26519 #define MA_SND_CHMAP_RC SND_CHMAP_RC
26520 #define MA_SND_CHMAP_FLC SND_CHMAP_FLC
26521 #define MA_SND_CHMAP_FRC SND_CHMAP_FRC
26522 #define MA_SND_CHMAP_RLC SND_CHMAP_RLC
26523 #define MA_SND_CHMAP_RRC SND_CHMAP_RRC
26524 #define MA_SND_CHMAP_FLW SND_CHMAP_FLW
26525 #define MA_SND_CHMAP_FRW SND_CHMAP_FRW
26526 #define MA_SND_CHMAP_FLH SND_CHMAP_FLH
26527 #define MA_SND_CHMAP_FCH SND_CHMAP_FCH
26528 #define MA_SND_CHMAP_FRH SND_CHMAP_FRH
26529 #define MA_SND_CHMAP_TC SND_CHMAP_TC
26530 #define MA_SND_CHMAP_TFL SND_CHMAP_TFL
26531 #define MA_SND_CHMAP_TFR SND_CHMAP_TFR
26532 #define MA_SND_CHMAP_TFC SND_CHMAP_TFC
26533 #define MA_SND_CHMAP_TRL SND_CHMAP_TRL
26534 #define MA_SND_CHMAP_TRR SND_CHMAP_TRR
26535 #define MA_SND_CHMAP_TRC SND_CHMAP_TRC
26536 #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
26537 #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
26538 #define MA_SND_CHMAP_TSL SND_CHMAP_TSL
26539 #define MA_SND_CHMAP_TSR SND_CHMAP_TSR
26540 #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
26541 #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
26542 #define MA_SND_CHMAP_BC SND_CHMAP_BC
26543 #define MA_SND_CHMAP_BLC SND_CHMAP_BLC
26544 #define MA_SND_CHMAP_BRC SND_CHMAP_BRC
26545
26546 /* Open mode flags. */
26547 #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
26548 #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
26549 #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
26550 #else
26551 #include <errno.h> /* For EPIPE, etc. */
26552 typedef unsigned long ma_snd_pcm_uframes_t;
26553 typedef long ma_snd_pcm_sframes_t;
26554 typedef int ma_snd_pcm_stream_t;
26555 typedef int ma_snd_pcm_format_t;
26556 typedef int ma_snd_pcm_access_t;
26557 typedef int ma_snd_pcm_state_t;
26558 typedef struct ma_snd_pcm_t ma_snd_pcm_t;
26559 typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
26560 typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
26561 typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
26562 typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
26563 typedef struct
26564 {
26565 void* addr;
26566 unsigned int first;
26567 unsigned int step;
26568 } ma_snd_pcm_channel_area_t;
26569 typedef struct
26570 {
26571 unsigned int channels;
26572 unsigned int pos[1];
26573 } ma_snd_pcm_chmap_t;
26574
26575 /* snd_pcm_state_t */
26576 #define MA_SND_PCM_STATE_OPEN 0
26577 #define MA_SND_PCM_STATE_SETUP 1
26578 #define MA_SND_PCM_STATE_PREPARED 2
26579 #define MA_SND_PCM_STATE_RUNNING 3
26580 #define MA_SND_PCM_STATE_XRUN 4
26581 #define MA_SND_PCM_STATE_DRAINING 5
26582 #define MA_SND_PCM_STATE_PAUSED 6
26583 #define MA_SND_PCM_STATE_SUSPENDED 7
26584 #define MA_SND_PCM_STATE_DISCONNECTED 8
26585
26586 /* snd_pcm_stream_t */
26587 #define MA_SND_PCM_STREAM_PLAYBACK 0
26588 #define MA_SND_PCM_STREAM_CAPTURE 1
26589
26590 /* snd_pcm_format_t */
26591 #define MA_SND_PCM_FORMAT_UNKNOWN -1
26592 #define MA_SND_PCM_FORMAT_U8 1
26593 #define MA_SND_PCM_FORMAT_S16_LE 2
26594 #define MA_SND_PCM_FORMAT_S16_BE 3
26595 #define MA_SND_PCM_FORMAT_S24_LE 6
26596 #define MA_SND_PCM_FORMAT_S24_BE 7
26597 #define MA_SND_PCM_FORMAT_S32_LE 10
26598 #define MA_SND_PCM_FORMAT_S32_BE 11
26599 #define MA_SND_PCM_FORMAT_FLOAT_LE 14
26600 #define MA_SND_PCM_FORMAT_FLOAT_BE 15
26601 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16
26602 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17
26603 #define MA_SND_PCM_FORMAT_MU_LAW 20
26604 #define MA_SND_PCM_FORMAT_A_LAW 21
26605 #define MA_SND_PCM_FORMAT_S24_3LE 32
26606 #define MA_SND_PCM_FORMAT_S24_3BE 33
26607
26608 /* snd_pcm_access_t */
26609 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
26610 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
26611 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
26612 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
26613 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
26614
26615 /* Channel positions. */
26616 #define MA_SND_CHMAP_UNKNOWN 0
26617 #define MA_SND_CHMAP_NA 1
26618 #define MA_SND_CHMAP_MONO 2
26619 #define MA_SND_CHMAP_FL 3
26620 #define MA_SND_CHMAP_FR 4
26621 #define MA_SND_CHMAP_RL 5
26622 #define MA_SND_CHMAP_RR 6
26623 #define MA_SND_CHMAP_FC 7
26624 #define MA_SND_CHMAP_LFE 8
26625 #define MA_SND_CHMAP_SL 9
26626 #define MA_SND_CHMAP_SR 10
26627 #define MA_SND_CHMAP_RC 11
26628 #define MA_SND_CHMAP_FLC 12
26629 #define MA_SND_CHMAP_FRC 13
26630 #define MA_SND_CHMAP_RLC 14
26631 #define MA_SND_CHMAP_RRC 15
26632 #define MA_SND_CHMAP_FLW 16
26633 #define MA_SND_CHMAP_FRW 17
26634 #define MA_SND_CHMAP_FLH 18
26635 #define MA_SND_CHMAP_FCH 19
26636 #define MA_SND_CHMAP_FRH 20
26637 #define MA_SND_CHMAP_TC 21
26638 #define MA_SND_CHMAP_TFL 22
26639 #define MA_SND_CHMAP_TFR 23
26640 #define MA_SND_CHMAP_TFC 24
26641 #define MA_SND_CHMAP_TRL 25
26642 #define MA_SND_CHMAP_TRR 26
26643 #define MA_SND_CHMAP_TRC 27
26644 #define MA_SND_CHMAP_TFLC 28
26645 #define MA_SND_CHMAP_TFRC 29
26646 #define MA_SND_CHMAP_TSL 30
26647 #define MA_SND_CHMAP_TSR 31
26648 #define MA_SND_CHMAP_LLFE 32
26649 #define MA_SND_CHMAP_RLFE 33
26650 #define MA_SND_CHMAP_BC 34
26651 #define MA_SND_CHMAP_BLC 35
26652 #define MA_SND_CHMAP_BRC 36
26653
26654 /* Open mode flags. */
26655 #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
26656 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
26657 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
26658 #endif
26659
26660 typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
26661 typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
26662 typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
26663 typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
26664 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);
26665 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);
26666 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);
26667 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);
26668 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);
26669 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);
26670 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);
26671 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);
26672 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);
26673 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);
26674 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);
26675 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);
26676 typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
26677 typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
26678 typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
26679 typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
26680 typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
26681 typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
26682 typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
26683 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);
26684 typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
26685 typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
26686 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);
26687 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);
26688 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);
26689 typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
26690 typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
26691 typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
26692 typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
26693 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);
26694 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);
26695 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);
26696 typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
26697 typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
26698 typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
26699 typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
26700 typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
26701 typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
26702 typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
26703 typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
26704 typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
26705 typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm);
26706 typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
26707 typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
26708 typedef int (* ma_snd_card_get_index_proc) (const char *name);
26709 typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
26710 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);
26711 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);
26712 typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
26713 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
26714 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);
26715 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
26716 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
26717 typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
26718 typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock);
26719 typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
26720 typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
26721 typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
26722 typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
26723 typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm);
26724 typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
26725 typedef int (* ma_snd_config_update_free_global_proc) (void);
26726
26727 /* This array specifies each of the common devices that can be used for both playback and capture. */
26728 static const char* g_maCommonDeviceNamesALSA[] = {
26729 "default",
26730 "null",
26731 "pulse",
26732 "jack"
26733 };
26734
26735 /* This array allows us to blacklist specific playback devices. */
26736 static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
26737 ""
26738 };
26739
26740 /* This array allows us to blacklist specific capture devices. */
26741 static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
26742 ""
26743 };
26744
26745
26746 static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
26747 {
26748 ma_snd_pcm_format_t ALSAFormats[] = {
26749 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
26750 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
26751 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
26752 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
26753 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
26754 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
26755 };
26756
26757 if (ma_is_big_endian()) {
26758 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
26759 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
26760 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
26761 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
26762 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
26763 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
26764 }
26765
26766 return ALSAFormats[format];
26767 }
26768
26769 static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
26770 {
26771 if (ma_is_little_endian()) {
26772 switch (formatALSA) {
26773 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
26774 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
26775 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
26776 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
26777 default: break;
26778 }
26779 } else {
26780 switch (formatALSA) {
26781 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
26782 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
26783 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
26784 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
26785 default: break;
26786 }
26787 }
26788
26789 /* Endian agnostic. */
26790 switch (formatALSA) {
26791 case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
26792 default: return ma_format_unknown;
26793 }
26794 }
26795
26796 static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
26797 {
26798 switch (alsaChannelPos)
26799 {
26800 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
26801 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
26802 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
26803 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
26804 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
26805 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
26806 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
26807 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
26808 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
26809 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
26810 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
26811 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
26812 case MA_SND_CHMAP_RLC: return 0;
26813 case MA_SND_CHMAP_RRC: return 0;
26814 case MA_SND_CHMAP_FLW: return 0;
26815 case MA_SND_CHMAP_FRW: return 0;
26816 case MA_SND_CHMAP_FLH: return 0;
26817 case MA_SND_CHMAP_FCH: return 0;
26818 case MA_SND_CHMAP_FRH: return 0;
26819 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
26820 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
26821 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
26822 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
26823 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
26824 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
26825 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
26826 default: break;
26827 }
26828
26829 return 0;
26830 }
26831
26832 static ma_bool32 ma_is_common_device_name__alsa(const char* name)
26833 {
26834 size_t iName;
26835 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
26836 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
26837 return MA_TRUE;
26838 }
26839 }
26840
26841 return MA_FALSE;
26842 }
26843
26844
26845 static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
26846 {
26847 size_t iName;
26848 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
26849 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
26850 return MA_TRUE;
26851 }
26852 }
26853
26854 return MA_FALSE;
26855 }
26856
26857 static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
26858 {
26859 size_t iName;
26860 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
26861 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
26862 return MA_TRUE;
26863 }
26864 }
26865
26866 return MA_FALSE;
26867 }
26868
26869 static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
26870 {
26871 if (deviceType == ma_device_type_playback) {
26872 return ma_is_playback_device_blacklisted__alsa(name);
26873 } else {
26874 return ma_is_capture_device_blacklisted__alsa(name);
26875 }
26876 }
26877
26878
26879 static const char* ma_find_char(const char* str, char c, int* index)
26880 {
26881 int i = 0;
26882 for (;;) {
26883 if (str[i] == '\0') {
26884 if (index) *index = -1;
26885 return NULL;
26886 }
26887
26888 if (str[i] == c) {
26889 if (index) *index = i;
26890 return str + i;
26891 }
26892
26893 i += 1;
26894 }
26895
26896 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
26897 if (index) *index = -1;
26898 return NULL;
26899 }
26900
26901 static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
26902 {
26903 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
26904
26905 int commaPos;
26906 const char* dev;
26907 int i;
26908
26909 if (hwid == NULL) {
26910 return MA_FALSE;
26911 }
26912
26913 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
26914 return MA_FALSE;
26915 }
26916
26917 hwid += 3;
26918
26919 dev = ma_find_char(hwid, ',', &commaPos);
26920 if (dev == NULL) {
26921 return MA_FALSE;
26922 } else {
26923 dev += 1; /* Skip past the ",". */
26924 }
26925
26926 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
26927 for (i = 0; i < commaPos; ++i) {
26928 if (hwid[i] < '0' || hwid[i] > '9') {
26929 return MA_FALSE;
26930 }
26931 }
26932
26933 /* Check if everything after the "," is numeric. If not, return false. */
26934 i = 0;
26935 while (dev[i] != '\0') {
26936 if (dev[i] < '0' || dev[i] > '9') {
26937 return MA_FALSE;
26938 }
26939 i += 1;
26940 }
26941
26942 return MA_TRUE;
26943 }
26944
26945 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. */
26946 {
26947 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
26948
26949 int colonPos;
26950 int commaPos;
26951 char card[256];
26952 const char* dev;
26953 int cardIndex;
26954
26955 if (dst == NULL) {
26956 return -1;
26957 }
26958 if (dstSize < 7) {
26959 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
26960 }
26961
26962 *dst = '\0'; /* Safety. */
26963 if (src == NULL) {
26964 return -1;
26965 }
26966
26967 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
26968 if (ma_is_device_name_in_hw_format__alsa(src)) {
26969 return ma_strcpy_s(dst, dstSize, src);
26970 }
26971
26972 src = ma_find_char(src, ':', &colonPos);
26973 if (src == NULL) {
26974 return -1; /* Couldn't find a colon */
26975 }
26976
26977 dev = ma_find_char(src, ',', &commaPos);
26978 if (dev == NULL) {
26979 dev = "0";
26980 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
26981 } else {
26982 dev = dev + 5; /* +5 = ",DEV=" */
26983 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
26984 }
26985
26986 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
26987 if (cardIndex < 0) {
26988 return -2; /* Failed to retrieve the card index. */
26989 }
26990
26991
26992 /* Construction. */
26993 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
26994 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
26995 return -3;
26996 }
26997 if (ma_strcat_s(dst, dstSize, ",") != 0) {
26998 return -3;
26999 }
27000 if (ma_strcat_s(dst, dstSize, dev) != 0) {
27001 return -3;
27002 }
27003
27004 return 0;
27005 }
27006
27007 static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
27008 {
27009 ma_uint32 i;
27010
27011 MA_ASSERT(pHWID != NULL);
27012
27013 for (i = 0; i < count; ++i) {
27014 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
27015 return MA_TRUE;
27016 }
27017 }
27018
27019 return MA_FALSE;
27020 }
27021
27022
27023 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)
27024 {
27025 ma_snd_pcm_t* pPCM;
27026 ma_snd_pcm_stream_t stream;
27027
27028 MA_ASSERT(pContext != NULL);
27029 MA_ASSERT(ppPCM != NULL);
27030
27031 *ppPCM = NULL;
27032 pPCM = NULL;
27033
27034 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
27035
27036 if (pDeviceID == NULL) {
27037 ma_bool32 isDeviceOpen;
27038 size_t i;
27039
27040 /*
27041 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
27042 me feel better to try as hard as we can get to get _something_ working.
27043 */
27044 const char* defaultDeviceNames[] = {
27045 "default",
27046 NULL,
27047 NULL,
27048 NULL,
27049 NULL,
27050 NULL,
27051 NULL
27052 };
27053
27054 if (shareMode == ma_share_mode_exclusive) {
27055 defaultDeviceNames[1] = "hw";
27056 defaultDeviceNames[2] = "hw:0";
27057 defaultDeviceNames[3] = "hw:0,0";
27058 } else {
27059 if (deviceType == ma_device_type_playback) {
27060 defaultDeviceNames[1] = "dmix";
27061 defaultDeviceNames[2] = "dmix:0";
27062 defaultDeviceNames[3] = "dmix:0,0";
27063 } else {
27064 defaultDeviceNames[1] = "dsnoop";
27065 defaultDeviceNames[2] = "dsnoop:0";
27066 defaultDeviceNames[3] = "dsnoop:0,0";
27067 }
27068 defaultDeviceNames[4] = "hw";
27069 defaultDeviceNames[5] = "hw:0";
27070 defaultDeviceNames[6] = "hw:0,0";
27071 }
27072
27073 isDeviceOpen = MA_FALSE;
27074 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
27075 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
27076 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
27077 isDeviceOpen = MA_TRUE;
27078 break;
27079 }
27080 }
27081 }
27082
27083 if (!isDeviceOpen) {
27084 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.");
27085 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
27086 }
27087 } else {
27088 /*
27089 We're trying to open a specific device. There's a few things to consider here:
27090
27091 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
27092 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
27093 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").
27094 */
27095
27096 /* May end up needing to make small adjustments to the ID, so make a copy. */
27097 ma_device_id deviceID = *pDeviceID;
27098 int resultALSA = -ENODEV;
27099
27100 if (deviceID.alsa[0] != ':') {
27101 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
27102 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
27103 } else {
27104 char hwid[256];
27105
27106 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
27107 if (deviceID.alsa[1] == '\0') {
27108 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
27109 }
27110
27111 if (shareMode == ma_share_mode_shared) {
27112 if (deviceType == ma_device_type_playback) {
27113 ma_strcpy_s(hwid, sizeof(hwid), "dmix");
27114 } else {
27115 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
27116 }
27117
27118 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
27119 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
27120 }
27121 }
27122
27123 /* 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. */
27124 if (resultALSA != 0) {
27125 ma_strcpy_s(hwid, sizeof(hwid), "hw");
27126 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
27127 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
27128 }
27129 }
27130 }
27131
27132 if (resultALSA < 0) {
27133 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.");
27134 return ma_result_from_errno(-resultALSA);
27135 }
27136 }
27137
27138 *ppPCM = pPCM;
27139 return MA_SUCCESS;
27140 }
27141
27142
27143 static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
27144 {
27145 int resultALSA;
27146 ma_bool32 cbResult = MA_TRUE;
27147 char** ppDeviceHints;
27148 ma_device_id* pUniqueIDs = NULL;
27149 ma_uint32 uniqueIDCount = 0;
27150 char** ppNextDeviceHint;
27151
27152 MA_ASSERT(pContext != NULL);
27153 MA_ASSERT(callback != NULL);
27154
27155 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
27156
27157 resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
27158 if (resultALSA < 0) {
27159 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
27160 return ma_result_from_errno(-resultALSA);
27161 }
27162
27163 ppNextDeviceHint = ppDeviceHints;
27164 while (*ppNextDeviceHint != NULL) {
27165 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
27166 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
27167 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
27168 ma_device_type deviceType = ma_device_type_playback;
27169 ma_bool32 stopEnumeration = MA_FALSE;
27170 char hwid[sizeof(pUniqueIDs->alsa)];
27171 ma_device_info deviceInfo;
27172
27173 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
27174 deviceType = ma_device_type_playback;
27175 }
27176 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
27177 deviceType = ma_device_type_capture;
27178 }
27179
27180 if (NAME != NULL) {
27181 if (pContext->alsa.useVerboseDeviceEnumeration) {
27182 /* Verbose mode. Use the name exactly as-is. */
27183 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
27184 } else {
27185 /* Simplified mode. Use ":%d,%d" format. */
27186 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
27187 /*
27188 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
27189 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
27190 initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
27191 device type and sharing mode.
27192 */
27193 char* dst = hwid;
27194 char* src = hwid+2;
27195 while ((*dst++ = *src++));
27196 } else {
27197 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
27198 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
27199 }
27200
27201 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
27202 goto next_device; /* The device has already been enumerated. Move on to the next one. */
27203 } else {
27204 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
27205 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
27206 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks);
27207 if (pNewUniqueIDs == NULL) {
27208 goto next_device; /* Failed to allocate memory. */
27209 }
27210
27211 pUniqueIDs = pNewUniqueIDs;
27212 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
27213 uniqueIDCount += 1;
27214 }
27215 }
27216 } else {
27217 MA_ZERO_MEMORY(hwid, sizeof(hwid));
27218 }
27219
27220 MA_ZERO_OBJECT(&deviceInfo);
27221 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
27222
27223 /*
27224 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
27225 just use the name of "default" as the indicator.
27226 */
27227 if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) {
27228 deviceInfo.isDefault = MA_TRUE;
27229 }
27230
27231
27232 /*
27233 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
27234 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
27235 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
27236 description.
27237
27238 The value in DESC seems to be split into two lines, with the first line being the name of the device and the
27239 second line being a description of the device. I don't like having the description be across two lines because
27240 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
27241 being put into parentheses. In simplified mode I'm just stripping the second line entirely.
27242 */
27243 if (DESC != NULL) {
27244 int lfPos;
27245 const char* line2 = ma_find_char(DESC, '\n', &lfPos);
27246 if (line2 != NULL) {
27247 line2 += 1; /* Skip past the new-line character. */
27248
27249 if (pContext->alsa.useVerboseDeviceEnumeration) {
27250 /* Verbose mode. Put the second line in brackets. */
27251 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
27252 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
27253 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
27254 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
27255 } else {
27256 /* Simplified mode. Strip the second line entirely. */
27257 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
27258 }
27259 } else {
27260 /* There's no second line. Just copy the whole description. */
27261 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
27262 }
27263 }
27264
27265 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
27266 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
27267 }
27268
27269 /*
27270 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
27271 again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which
27272 means both Input and Output.
27273 */
27274 if (cbResult) {
27275 if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) {
27276 if (deviceType == ma_device_type_playback) {
27277 if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
27278 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
27279 }
27280 } else {
27281 if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
27282 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
27283 }
27284 }
27285 }
27286 }
27287
27288 if (cbResult == MA_FALSE) {
27289 stopEnumeration = MA_TRUE;
27290 }
27291
27292 next_device:
27293 free(NAME);
27294 free(DESC);
27295 free(IOID);
27296 ppNextDeviceHint += 1;
27297
27298 /* We need to stop enumeration if the callback returned false. */
27299 if (stopEnumeration) {
27300 break;
27301 }
27302 }
27303
27304 ma_free(pUniqueIDs, &pContext->allocationCallbacks);
27305 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
27306
27307 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
27308
27309 return MA_SUCCESS;
27310 }
27311
27312
27313 typedef struct
27314 {
27315 ma_device_type deviceType;
27316 const ma_device_id* pDeviceID;
27317 ma_share_mode shareMode;
27318 ma_device_info* pDeviceInfo;
27319 ma_bool32 foundDevice;
27320 } ma_context_get_device_info_enum_callback_data__alsa;
27321
27322 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)
27323 {
27324 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
27325 MA_ASSERT(pData != NULL);
27326
27327 (void)pContext;
27328
27329 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
27330 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
27331 pData->foundDevice = MA_TRUE;
27332 } else {
27333 if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {
27334 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
27335 pData->foundDevice = MA_TRUE;
27336 }
27337 }
27338
27339 /* Keep enumerating until we have found the device. */
27340 return !pData->foundDevice;
27341 }
27342
27343 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)
27344 {
27345 MA_ASSERT(pPCM != NULL);
27346 MA_ASSERT(pHWParams != NULL);
27347 MA_ASSERT(pDeviceInfo != NULL);
27348
27349 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) {
27350 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
27351 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
27352 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
27353 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
27354 pDeviceInfo->nativeDataFormatCount += 1;
27355 }
27356 }
27357
27358 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)
27359 {
27360 ma_uint32 iSampleRate;
27361 unsigned int minSampleRate;
27362 unsigned int maxSampleRate;
27363 int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */
27364
27365 /* There could be a range. */
27366 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
27367 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
27368
27369 /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */
27370 minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
27371 maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
27372
27373 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
27374 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
27375
27376 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
27377 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);
27378 }
27379 }
27380
27381 /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */
27382 if (!ma_is_standard_sample_rate(minSampleRate)) {
27383 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);
27384 }
27385
27386 if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {
27387 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);
27388 }
27389 }
27390
27391 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)
27392 {
27393 ma_context_get_device_info_enum_callback_data__alsa data;
27394 ma_result result;
27395 int resultALSA;
27396 ma_snd_pcm_t* pPCM;
27397 ma_snd_pcm_hw_params_t* pHWParams;
27398 ma_uint32 iFormat;
27399 ma_uint32 iChannel;
27400
27401 MA_ASSERT(pContext != NULL);
27402
27403 /* We just enumerate to find basic information about the device. */
27404 data.deviceType = deviceType;
27405 data.pDeviceID = pDeviceID;
27406 data.pDeviceInfo = pDeviceInfo;
27407 data.foundDevice = MA_FALSE;
27408 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
27409 if (result != MA_SUCCESS) {
27410 return result;
27411 }
27412
27413 if (!data.foundDevice) {
27414 return MA_NO_DEVICE;
27415 }
27416
27417 if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
27418 pDeviceInfo->isDefault = MA_TRUE;
27419 }
27420
27421 /* For detailed info we need to open the device. */
27422 result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);
27423 if (result != MA_SUCCESS) {
27424 return result;
27425 }
27426
27427 /* We need to initialize a HW parameters object in order to know what formats are supported. */
27428 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);
27429 if (pHWParams == NULL) {
27430 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
27431 return MA_OUT_OF_MEMORY;
27432 }
27433
27434 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27435 if (resultALSA < 0) {
27436 ma_free(pHWParams, &pContext->allocationCallbacks);
27437 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
27438 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
27439 return ma_result_from_errno(-resultALSA);
27440 }
27441
27442 /*
27443 Some ALSA devices can support many permutations of formats, channels and rates. We only support
27444 a fixed number of permutations which means we need to employ some strategies to ensure the best
27445 combinations are returned. An example is the "pulse" device which can do it's own data conversion
27446 in software and as a result can support any combination of format, channels and rate.
27447
27448 We want to ensure the the first data formats are the best. We have a list of favored sample
27449 formats and sample rates, so these will be the basis of our iteration.
27450 */
27451
27452 /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
27453 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
27454 ma_format format = g_maFormatPriorities[iFormat];
27455
27456 /*
27457 For each format we need to make sure we reset the configuration space so we don't return
27458 channel counts and rates that aren't compatible with a format.
27459 */
27460 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27461
27462 /* Test the format first. If this fails it means the format is not supported and we can skip it. */
27463 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) {
27464 /* The format is supported. */
27465 unsigned int minChannels;
27466 unsigned int maxChannels;
27467
27468 /*
27469 The configuration space needs to be restricted to this format so we can get an accurate
27470 picture of which sample rates and channel counts are support with this format.
27471 */
27472 ((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));
27473
27474 /* Now we need to check for supported channels. */
27475 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);
27476 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);
27477
27478 if (minChannels > MA_MAX_CHANNELS) {
27479 continue; /* Too many channels. */
27480 }
27481 if (maxChannels < MA_MIN_CHANNELS) {
27482 continue; /* Not enough channels. */
27483 }
27484
27485 /*
27486 Make sure the channel count is clamped. This is mainly intended for the max channels
27487 because some devices can report an unbound maximum.
27488 */
27489 minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
27490 maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
27491
27492 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
27493 /* 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. */
27494 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. */
27495 } else {
27496 /* The device only supports a specific set of channels. We need to iterate over all of them. */
27497 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
27498 /* Test the channel before applying it to the configuration space. */
27499 unsigned int channels = iChannel;
27500
27501 /* Make sure our channel range is reset before testing again or else we'll always fail the test. */
27502 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27503 ((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));
27504
27505 if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {
27506 /* The channel count is supported. */
27507
27508 /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
27509 ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);
27510
27511 /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
27512 ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);
27513 } else {
27514 /* The channel count is not supported. Skip. */
27515 }
27516 }
27517 }
27518 } else {
27519 /* The format is not supported. Skip. */
27520 }
27521 }
27522
27523 ma_free(pHWParams, &pContext->allocationCallbacks);
27524
27525 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
27526 return MA_SUCCESS;
27527 }
27528
27529 static ma_result ma_device_uninit__alsa(ma_device* pDevice)
27530 {
27531 MA_ASSERT(pDevice != NULL);
27532
27533 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
27534 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
27535 close(pDevice->alsa.wakeupfdCapture);
27536 ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
27537 }
27538
27539 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
27540 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
27541 close(pDevice->alsa.wakeupfdPlayback);
27542 ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
27543 }
27544
27545 return MA_SUCCESS;
27546 }
27547
27548 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)
27549 {
27550 ma_result result;
27551 int resultALSA;
27552 ma_snd_pcm_t* pPCM;
27553 ma_bool32 isUsingMMap;
27554 ma_snd_pcm_format_t formatALSA;
27555 ma_format internalFormat;
27556 ma_uint32 internalChannels;
27557 ma_uint32 internalSampleRate;
27558 ma_channel internalChannelMap[MA_MAX_CHANNELS];
27559 ma_uint32 internalPeriodSizeInFrames;
27560 ma_uint32 internalPeriods;
27561 int openMode;
27562 ma_snd_pcm_hw_params_t* pHWParams;
27563 ma_snd_pcm_sw_params_t* pSWParams;
27564 ma_snd_pcm_uframes_t bufferBoundary;
27565 int pollDescriptorCount;
27566 struct pollfd* pPollDescriptors;
27567 int wakeupfd;
27568
27569 MA_ASSERT(pConfig != NULL);
27570 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
27571 MA_ASSERT(pDevice != NULL);
27572
27573 formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);
27574
27575 openMode = 0;
27576 if (pConfig->alsa.noAutoResample) {
27577 openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
27578 }
27579 if (pConfig->alsa.noAutoChannels) {
27580 openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
27581 }
27582 if (pConfig->alsa.noAutoFormat) {
27583 openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
27584 }
27585
27586 result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);
27587 if (result != MA_SUCCESS) {
27588 return result;
27589 }
27590
27591
27592 /* Hardware parameters. */
27593 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);
27594 if (pHWParams == NULL) {
27595 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27596 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters.");
27597 return MA_OUT_OF_MEMORY;
27598 }
27599
27600 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
27601 if (resultALSA < 0) {
27602 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27603 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27604 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
27605 return ma_result_from_errno(-resultALSA);
27606 }
27607
27608 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
27609 isUsingMMap = MA_FALSE;
27610 #if 0 /* NOTE: MMAP mode temporarily disabled. */
27611 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. */
27612 if (!pConfig->alsa.noMMap) {
27613 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) {
27614 pDevice->alsa.isUsingMMap = MA_TRUE;
27615 }
27616 }
27617 }
27618 #endif
27619
27620 if (!isUsingMMap) {
27621 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);
27622 if (resultALSA < 0) {
27623 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27624 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27625 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.");
27626 return ma_result_from_errno(-resultALSA);
27627 }
27628 }
27629
27630 /*
27631 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
27632 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
27633 */
27634
27635 /* Format. */
27636 {
27637 /*
27638 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
27639 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.
27640 */
27641 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) {
27642 /* We're either requesting the native format or the specified format is not supported. */
27643 size_t iFormat;
27644
27645 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
27646 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
27647 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) {
27648 formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);
27649 break;
27650 }
27651 }
27652
27653 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
27654 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27655 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27656 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.");
27657 return MA_FORMAT_NOT_SUPPORTED;
27658 }
27659 }
27660
27661 resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
27662 if (resultALSA < 0) {
27663 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27664 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27665 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.");
27666 return ma_result_from_errno(-resultALSA);
27667 }
27668
27669 internalFormat = ma_format_from_alsa(formatALSA);
27670 if (internalFormat == ma_format_unknown) {
27671 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27672 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27673 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.");
27674 return MA_FORMAT_NOT_SUPPORTED;
27675 }
27676 }
27677
27678 /* Channels. */
27679 {
27680 unsigned int channels = pDescriptor->channels;
27681 if (channels == 0) {
27682 channels = MA_DEFAULT_CHANNELS;
27683 }
27684
27685 resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
27686 if (resultALSA < 0) {
27687 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27688 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27689 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.");
27690 return ma_result_from_errno(-resultALSA);
27691 }
27692
27693 internalChannels = (ma_uint32)channels;
27694 }
27695
27696 /* Sample Rate */
27697 {
27698 unsigned int sampleRate;
27699
27700 /*
27701 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
27702 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
27703 resampling.
27704
27705 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
27706 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
27707 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
27708 faster rate.
27709
27710 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
27711 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
27712 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
27713
27714 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
27715 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
27716 */
27717 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
27718
27719 sampleRate = pDescriptor->sampleRate;
27720 if (sampleRate == 0) {
27721 sampleRate = MA_DEFAULT_SAMPLE_RATE;
27722 }
27723
27724 resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
27725 if (resultALSA < 0) {
27726 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27727 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27728 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.");
27729 return ma_result_from_errno(-resultALSA);
27730 }
27731
27732 internalSampleRate = (ma_uint32)sampleRate;
27733 }
27734
27735 /* Periods. */
27736 {
27737 ma_uint32 periods = pDescriptor->periodCount;
27738
27739 resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
27740 if (resultALSA < 0) {
27741 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27742 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27743 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.");
27744 return ma_result_from_errno(-resultALSA);
27745 }
27746
27747 internalPeriods = periods;
27748 }
27749
27750 /* Buffer Size */
27751 {
27752 ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;
27753
27754 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);
27755 if (resultALSA < 0) {
27756 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27757 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27758 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.");
27759 return ma_result_from_errno(-resultALSA);
27760 }
27761
27762 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
27763 }
27764
27765 /* Apply hardware parameters. */
27766 resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
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 hardware parameters. snd_pcm_hw_params() failed.");
27771 return ma_result_from_errno(-resultALSA);
27772 }
27773
27774 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
27775 pHWParams = NULL;
27776
27777
27778 /* Software parameters. */
27779 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);
27780 if (pSWParams == NULL) {
27781 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27782 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters.");
27783 return MA_OUT_OF_MEMORY;
27784 }
27785
27786 resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
27787 if (resultALSA < 0) {
27788 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27789 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27790 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.");
27791 return ma_result_from_errno(-resultALSA);
27792 }
27793
27794 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));
27795 if (resultALSA < 0) {
27796 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27797 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27798 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.");
27799 return ma_result_from_errno(-resultALSA);
27800 }
27801
27802 resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
27803 if (resultALSA < 0) {
27804 bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
27805 }
27806
27807 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
27808 /*
27809 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
27810 the size of a period. But for full-duplex we need to set it such that it is at least two periods.
27811 */
27812 resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
27813 if (resultALSA < 0) {
27814 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27815 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27816 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.");
27817 return ma_result_from_errno(-resultALSA);
27818 }
27819
27820 resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
27821 if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
27822 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27823 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27824 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.");
27825 return ma_result_from_errno(-resultALSA);
27826 }
27827 }
27828
27829 resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
27830 if (resultALSA < 0) {
27831 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27832 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27833 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.");
27834 return ma_result_from_errno(-resultALSA);
27835 }
27836
27837 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
27838 pSWParams = NULL;
27839
27840
27841 /* 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. */
27842 {
27843 ma_snd_pcm_chmap_t* pChmap = NULL;
27844 if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) {
27845 pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
27846 }
27847
27848 if (pChmap != NULL) {
27849 ma_uint32 iChannel;
27850
27851 /* 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(). */
27852 if (pChmap->channels >= internalChannels) {
27853 /* Drop excess channels. */
27854 for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
27855 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
27856 }
27857 } else {
27858 ma_uint32 i;
27859
27860 /*
27861 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
27862 channels. If validation fails, fall back to defaults.
27863 */
27864 ma_bool32 isValid = MA_TRUE;
27865
27866 /* Fill with defaults. */
27867 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
27868
27869 /* Overwrite first pChmap->channels channels. */
27870 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
27871 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
27872 }
27873
27874 /* Validate. */
27875 for (i = 0; i < internalChannels && isValid; ++i) {
27876 ma_uint32 j;
27877 for (j = i+1; j < internalChannels; ++j) {
27878 if (internalChannelMap[i] == internalChannelMap[j]) {
27879 isValid = MA_FALSE;
27880 break;
27881 }
27882 }
27883 }
27884
27885 /* If our channel map is invalid, fall back to defaults. */
27886 if (!isValid) {
27887 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
27888 }
27889 }
27890
27891 free(pChmap);
27892 pChmap = NULL;
27893 } else {
27894 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
27895 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
27896 }
27897 }
27898
27899
27900 /*
27901 We need to retrieve the poll descriptors so we can use poll() to wait for data to become
27902 available for reading or writing. There's no well defined maximum for this so we're just going
27903 to allocate this on the heap.
27904 */
27905 pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
27906 if (pollDescriptorCount <= 0) {
27907 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27908 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.");
27909 return MA_ERROR;
27910 }
27911
27912 pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */
27913 if (pPollDescriptors == NULL) {
27914 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27915 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.");
27916 return MA_OUT_OF_MEMORY;
27917 }
27918
27919 /*
27920 We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver
27921 never returns from writei() and readi(). This has been observed with the "pulse" device.
27922 */
27923 wakeupfd = eventfd(0, 0);
27924 if (wakeupfd < 0) {
27925 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
27926 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27927 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.");
27928 return ma_result_from_errno(errno);
27929 }
27930
27931 /* We'll place the wakeup fd at the start of the buffer. */
27932 pPollDescriptors[0].fd = wakeupfd;
27933 pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */
27934 pPollDescriptors[0].revents = 0;
27935
27936 /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */
27937 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. */
27938 if (pollDescriptorCount <= 0) {
27939 close(wakeupfd);
27940 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
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 retrieve poll descriptors.");
27943 return MA_ERROR;
27944 }
27945
27946 if (deviceType == ma_device_type_capture) {
27947 pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;
27948 pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;
27949 pDevice->alsa.wakeupfdCapture = wakeupfd;
27950 } else {
27951 pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;
27952 pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;
27953 pDevice->alsa.wakeupfdPlayback = wakeupfd;
27954 }
27955
27956
27957 /* We're done. Prepare the device. */
27958 resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
27959 if (resultALSA < 0) {
27960 close(wakeupfd);
27961 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
27962 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
27963 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.");
27964 return ma_result_from_errno(-resultALSA);
27965 }
27966
27967
27968 if (deviceType == ma_device_type_capture) {
27969 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
27970 pDevice->alsa.isUsingMMapCapture = isUsingMMap;
27971 } else {
27972 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
27973 pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
27974 }
27975
27976 pDescriptor->format = internalFormat;
27977 pDescriptor->channels = internalChannels;
27978 pDescriptor->sampleRate = internalSampleRate;
27979 ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
27980 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
27981 pDescriptor->periodCount = internalPeriods;
27982
27983 return MA_SUCCESS;
27984 }
27985
27986 static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
27987 {
27988 MA_ASSERT(pDevice != NULL);
27989
27990 MA_ZERO_OBJECT(&pDevice->alsa);
27991
27992 if (pConfig->deviceType == ma_device_type_loopback) {
27993 return MA_DEVICE_TYPE_NOT_SUPPORTED;
27994 }
27995
27996 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27997 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
27998 if (result != MA_SUCCESS) {
27999 return result;
28000 }
28001 }
28002
28003 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
28004 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
28005 if (result != MA_SUCCESS) {
28006 return result;
28007 }
28008 }
28009
28010 return MA_SUCCESS;
28011 }
28012
28013 static ma_result ma_device_start__alsa(ma_device* pDevice)
28014 {
28015 int resultALSA;
28016
28017 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28018 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
28019 if (resultALSA < 0) {
28020 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.");
28021 return ma_result_from_errno(-resultALSA);
28022 }
28023 }
28024
28025 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28026 /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */
28027 }
28028
28029 return MA_SUCCESS;
28030 }
28031
28032 static ma_result ma_device_stop__alsa(ma_device* pDevice)
28033 {
28034 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
28035 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
28036 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
28037 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n");
28038
28039 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
28040 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n");
28041 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
28042 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n");
28043 } else {
28044 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
28045 }
28046 }
28047
28048 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
28049 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n");
28050 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
28051 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n");
28052
28053 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
28054 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n");
28055 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
28056 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n");
28057 } else {
28058 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n");
28059 }
28060 }
28061
28062 return MA_SUCCESS;
28063 }
28064
28065 static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)
28066 {
28067 for (;;) {
28068 unsigned short revents;
28069 int resultALSA;
28070 int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
28071 if (resultPoll < 0) {
28072 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.");
28073 return ma_result_from_errno(errno);
28074 }
28075
28076 /*
28077 Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor
28078 has had it's POLLIN flag set. If so, we need to actually read the data and then exit
28079 function. The wakeup descriptor will be the first item in the descriptors buffer.
28080 */
28081 if ((pPollDescriptors[0].revents & POLLIN) != 0) {
28082 ma_uint64 t;
28083 int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */
28084 if (resultRead < 0) {
28085 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.");
28086 return ma_result_from_errno(errno);
28087 }
28088
28089 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n");
28090 return MA_DEVICE_NOT_STARTED;
28091 }
28092
28093 /*
28094 Getting here means that some data should be able to be read. We need to use ALSA to
28095 translate the revents flags for us.
28096 */
28097 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. */
28098 if (resultALSA < 0) {
28099 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.");
28100 return ma_result_from_errno(-resultALSA);
28101 }
28102
28103 if ((revents & POLLERR) != 0) {
28104 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected.");
28105 return ma_result_from_errno(errno);
28106 }
28107
28108 if ((revents & requiredEvent) == requiredEvent) {
28109 break; /* We're done. Data available for reading or writing. */
28110 }
28111 }
28112
28113 return MA_SUCCESS;
28114 }
28115
28116 static ma_result ma_device_wait_read__alsa(ma_device* pDevice)
28117 {
28118 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. */
28119 }
28120
28121 static ma_result ma_device_wait_write__alsa(ma_device* pDevice)
28122 {
28123 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. */
28124 }
28125
28126 static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
28127 {
28128 ma_snd_pcm_sframes_t resultALSA = 0;
28129
28130 MA_ASSERT(pDevice != NULL);
28131 MA_ASSERT(pFramesOut != NULL);
28132
28133 if (pFramesRead != NULL) {
28134 *pFramesRead = 0;
28135 }
28136
28137 while (ma_device_get_state(pDevice) == ma_device_state_started) {
28138 ma_result result;
28139
28140 /* 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. */
28141 result = ma_device_wait_read__alsa(pDevice);
28142 if (result != MA_SUCCESS) {
28143 return result;
28144 }
28145
28146 /* Getting here means we should have data available. */
28147 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
28148 if (resultALSA >= 0) {
28149 break; /* Success. */
28150 } else {
28151 if (resultALSA == -EAGAIN) {
28152 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/
28153 continue; /* Try again. */
28154 } else if (resultALSA == -EPIPE) {
28155 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n");
28156
28157 /* Overrun. Recover and try again. If this fails we need to return an error. */
28158 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
28159 if (resultALSA < 0) {
28160 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.");
28161 return ma_result_from_errno((int)-resultALSA);
28162 }
28163
28164 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
28165 if (resultALSA < 0) {
28166 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
28167 return ma_result_from_errno((int)-resultALSA);
28168 }
28169
28170 continue; /* Try reading again. */
28171 }
28172 }
28173 }
28174
28175 if (pFramesRead != NULL) {
28176 *pFramesRead = resultALSA;
28177 }
28178
28179 return MA_SUCCESS;
28180 }
28181
28182 static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
28183 {
28184 ma_snd_pcm_sframes_t resultALSA = 0;
28185
28186 MA_ASSERT(pDevice != NULL);
28187 MA_ASSERT(pFrames != NULL);
28188
28189 if (pFramesWritten != NULL) {
28190 *pFramesWritten = 0;
28191 }
28192
28193 while (ma_device_get_state(pDevice) == ma_device_state_started) {
28194 ma_result result;
28195
28196 /* 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. */
28197 result = ma_device_wait_write__alsa(pDevice);
28198 if (result != MA_SUCCESS) {
28199 return result;
28200 }
28201
28202 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
28203 if (resultALSA >= 0) {
28204 break; /* Success. */
28205 } else {
28206 if (resultALSA == -EAGAIN) {
28207 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/
28208 continue; /* Try again. */
28209 } else if (resultALSA == -EPIPE) {
28210 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n");
28211
28212 /* Underrun. Recover and try again. If this fails we need to return an error. */
28213 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). */
28214 if (resultALSA < 0) {
28215 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.");
28216 return ma_result_from_errno((int)-resultALSA);
28217 }
28218
28219 /*
28220 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
28221 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
28222 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
28223 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
28224 quite right here.
28225 */
28226 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
28227 if (resultALSA < 0) {
28228 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
28229 return ma_result_from_errno((int)-resultALSA);
28230 }
28231
28232 continue; /* Try writing again. */
28233 }
28234 }
28235 }
28236
28237 if (pFramesWritten != NULL) {
28238 *pFramesWritten = resultALSA;
28239 }
28240
28241 return MA_SUCCESS;
28242 }
28243
28244 static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)
28245 {
28246 ma_uint64 t = 1;
28247 int resultWrite = 0;
28248
28249 MA_ASSERT(pDevice != NULL);
28250
28251 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n");
28252
28253 /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */
28254 if (pDevice->alsa.pPollDescriptorsCapture != NULL) {
28255 resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));
28256 }
28257 if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {
28258 resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));
28259 }
28260
28261 if (resultWrite < 0) {
28262 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n");
28263 return ma_result_from_errno(errno);
28264 }
28265
28266 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n");
28267
28268 return MA_SUCCESS;
28269 }
28270
28271 static ma_result ma_context_uninit__alsa(ma_context* pContext)
28272 {
28273 MA_ASSERT(pContext != NULL);
28274 MA_ASSERT(pContext->backend == ma_backend_alsa);
28275
28276 /* Clean up memory for memory leak checkers. */
28277 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
28278
28279 #ifndef MA_NO_RUNTIME_LINKING
28280 ma_dlclose(pContext, pContext->alsa.asoundSO);
28281 #endif
28282
28283 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
28284
28285 return MA_SUCCESS;
28286 }
28287
28288 static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
28289 {
28290 ma_result result;
28291 #ifndef MA_NO_RUNTIME_LINKING
28292 const char* libasoundNames[] = {
28293 "libasound.so.2",
28294 "libasound.so"
28295 };
28296 size_t i;
28297
28298 for (i = 0; i < ma_countof(libasoundNames); ++i) {
28299 pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
28300 if (pContext->alsa.asoundSO != NULL) {
28301 break;
28302 }
28303 }
28304
28305 if (pContext->alsa.asoundSO == NULL) {
28306 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n");
28307 return MA_NO_BACKEND;
28308 }
28309
28310 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
28311 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
28312 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
28313 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
28314 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
28315 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
28316 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
28317 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels");
28318 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
28319 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax");
28320 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
28321 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate");
28322 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
28323 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near");
28324 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
28325 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
28326 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
28327 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
28328 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
28329 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
28330 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
28331 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
28332 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
28333 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
28334 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
28335 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
28336 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format");
28337 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels");
28338 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate");
28339 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
28340 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
28341 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
28342 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
28343 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
28344 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
28345 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
28346 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
28347 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
28348 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
28349 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
28350 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
28351 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
28352 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
28353 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
28354 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
28355 pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset");
28356 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
28357 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
28358 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
28359 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
28360 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
28361 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
28362 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
28363 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
28364 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
28365 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
28366 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
28367 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
28368 pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock");
28369 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
28370 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
28371 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
28372 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors");
28373 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count");
28374 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents");
28375 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
28376 #else
28377 /* The system below is just for type safety. */
28378 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
28379 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
28380 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
28381 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
28382 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
28383 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
28384 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
28385 ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels;
28386 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
28387 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
28388 ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate;
28389 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
28390 ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax;
28391 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;
28392 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
28393 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
28394 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
28395 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
28396 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
28397 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
28398 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
28399 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
28400 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
28401 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
28402 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
28403 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
28404 ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format;
28405 ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels;
28406 ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate;
28407 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
28408 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
28409 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
28410 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
28411 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
28412 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
28413 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
28414 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
28415 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
28416 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
28417 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
28418 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
28419 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
28420 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
28421 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
28422 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
28423 ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset;
28424 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
28425 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
28426 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
28427 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
28428 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
28429 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
28430 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
28431 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
28432 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
28433 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
28434 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
28435 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
28436 ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock;
28437 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
28438 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
28439 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
28440 ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors;
28441 ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count;
28442 ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents;
28443 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
28444
28445 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
28446 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
28447 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
28448 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
28449 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
28450 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
28451 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
28452 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels;
28453 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
28454 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;
28455 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
28456 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate;
28457 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
28458 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
28459 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
28460 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
28461 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
28462 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
28463 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
28464 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
28465 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
28466 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min;
28467 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max;
28468 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
28469 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
28470 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
28471 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format;
28472 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels;
28473 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate;
28474 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
28475 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
28476 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
28477 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
28478 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
28479 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
28480 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
28481 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
28482 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
28483 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
28484 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
28485 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
28486 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
28487 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
28488 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
28489 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
28490 pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset;
28491 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
28492 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
28493 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
28494 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
28495 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
28496 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
28497 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
28498 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
28499 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
28500 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
28501 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
28502 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
28503 pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock;
28504 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
28505 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
28506 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
28507 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
28508 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
28509 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
28510 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
28511 #endif
28512
28513 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
28514
28515 result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock);
28516 if (result != MA_SUCCESS) {
28517 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.");
28518 return result;
28519 }
28520
28521 pCallbacks->onContextInit = ma_context_init__alsa;
28522 pCallbacks->onContextUninit = ma_context_uninit__alsa;
28523 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
28524 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
28525 pCallbacks->onDeviceInit = ma_device_init__alsa;
28526 pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
28527 pCallbacks->onDeviceStart = ma_device_start__alsa;
28528 pCallbacks->onDeviceStop = ma_device_stop__alsa;
28529 pCallbacks->onDeviceRead = ma_device_read__alsa;
28530 pCallbacks->onDeviceWrite = ma_device_write__alsa;
28531 pCallbacks->onDeviceDataLoop = NULL;
28532 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
28533
28534 return MA_SUCCESS;
28535 }
28536 #endif /* ALSA */
28537
28538
28539
28540 /******************************************************************************
28541
28542 PulseAudio Backend
28543
28544 ******************************************************************************/
28545 #ifdef MA_HAS_PULSEAUDIO
28546 /*
28547 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
28548 in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
28549
28550 PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
28551 allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it
28552 appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or
28553 write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the
28554 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
28555 when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.
28556
28557 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
28558 get fun, and I don't mean that in a good way...
28559
28560 The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
28561 don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
28562 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
28563 all of PulseAudio's problems stem from.
28564
28565 When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
28566 vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called
28567 pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop
28568 because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use
28569 it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.
28570
28571 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
28572 to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded
28573 main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely
28574 specialized such as if you want to integrate it into your application's existing main loop infrastructure.
28575
28576 (EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.
28577 It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)
28578
28579 Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to
28580 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
28581 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
28582 is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if
28583 you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`
28584 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
28585 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.
28586 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.
28587 This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
28588 attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
28589
28590 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
28591 internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
28592 host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
28593
28594 Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
28595 The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
28596 `pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
28597 information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
28598 is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
28599 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
28600 context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
28601 All of that just to retrieve basic information about a device!
28602
28603 Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
28604 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
28605 choices in PulseAudio.
28606
28607 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
28608 because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
28609 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
28610 set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
28611 straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
28612 PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
28613 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
28614 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
28615 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
28616 that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
28617 stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
28618 callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
28619 doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
28620 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
28621 callback is not fired.
28622
28623 This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
28624 continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
28625 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
28626 PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
28627 `pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
28628 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
28629 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
28630 *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
28631 important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
28632 before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
28633 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!
28634
28635 This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
28636 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
28637 resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
28638 disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
28639 callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
28640
28641 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,
28642 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
28643 "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
28644 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
28645 guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
28646 absolutely beyond me. Would it really be that hard to just make it run synchronously?
28647
28648 Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
28649 they were initialized in.
28650
28651 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
28652 embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
28653 run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
28654 requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
28655 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
28656 parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
28657 changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
28658 */
28659
28660
28661 /*
28662 It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
28663 to check for type safety. We cannot do this when linking at run time because the header might not be available.
28664 */
28665 #ifdef MA_NO_RUNTIME_LINKING
28666
28667 /* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
28668 #if !defined(__cplusplus)
28669 #if defined(__STRICT_ANSI__)
28670 #if !defined(inline)
28671 #define inline __inline__ __attribute__((always_inline))
28672 #define MA_INLINE_DEFINED
28673 #endif
28674 #endif
28675 #endif
28676 #include <pulse/pulseaudio.h>
28677 #if defined(MA_INLINE_DEFINED)
28678 #undef inline
28679 #undef MA_INLINE_DEFINED
28680 #endif
28681
28682 #define MA_PA_OK PA_OK
28683 #define MA_PA_ERR_ACCESS PA_ERR_ACCESS
28684 #define MA_PA_ERR_INVALID PA_ERR_INVALID
28685 #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
28686 #define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
28687
28688 #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
28689 #define MA_PA_RATE_MAX PA_RATE_MAX
28690
28691 typedef pa_context_flags_t ma_pa_context_flags_t;
28692 #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
28693 #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
28694 #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
28695
28696 typedef pa_stream_flags_t ma_pa_stream_flags_t;
28697 #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
28698 #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
28699 #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
28700 #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
28701 #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
28702 #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
28703 #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
28704 #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
28705 #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
28706 #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
28707 #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
28708 #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
28709 #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
28710 #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
28711 #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
28712 #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
28713 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
28714 #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
28715 #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
28716 #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
28717 #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
28718
28719 typedef pa_sink_flags_t ma_pa_sink_flags_t;
28720 #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
28721 #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
28722 #define MA_PA_SINK_LATENCY PA_SINK_LATENCY
28723 #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
28724 #define MA_PA_SINK_NETWORK PA_SINK_NETWORK
28725 #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
28726 #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
28727 #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
28728 #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
28729 #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
28730
28731 typedef pa_source_flags_t ma_pa_source_flags_t;
28732 #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
28733 #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
28734 #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
28735 #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
28736 #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
28737 #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
28738 #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
28739 #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
28740 #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
28741
28742 typedef pa_context_state_t ma_pa_context_state_t;
28743 #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
28744 #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
28745 #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
28746 #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
28747 #define MA_PA_CONTEXT_READY PA_CONTEXT_READY
28748 #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
28749 #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
28750
28751 typedef pa_stream_state_t ma_pa_stream_state_t;
28752 #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
28753 #define MA_PA_STREAM_CREATING PA_STREAM_CREATING
28754 #define MA_PA_STREAM_READY PA_STREAM_READY
28755 #define MA_PA_STREAM_FAILED PA_STREAM_FAILED
28756 #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
28757
28758 typedef pa_operation_state_t ma_pa_operation_state_t;
28759 #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
28760 #define MA_PA_OPERATION_DONE PA_OPERATION_DONE
28761 #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
28762
28763 typedef pa_sink_state_t ma_pa_sink_state_t;
28764 #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
28765 #define MA_PA_SINK_RUNNING PA_SINK_RUNNING
28766 #define MA_PA_SINK_IDLE PA_SINK_IDLE
28767 #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
28768
28769 typedef pa_source_state_t ma_pa_source_state_t;
28770 #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
28771 #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
28772 #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
28773 #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
28774
28775 typedef pa_seek_mode_t ma_pa_seek_mode_t;
28776 #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
28777 #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
28778 #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
28779 #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
28780
28781 typedef pa_channel_position_t ma_pa_channel_position_t;
28782 #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
28783 #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
28784 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
28785 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
28786 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
28787 #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
28788 #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
28789 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
28790 #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
28791 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
28792 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
28793 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
28794 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
28795 #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
28796 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
28797 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
28798 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
28799 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
28800 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
28801 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
28802 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
28803 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
28804 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
28805 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
28806 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
28807 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
28808 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
28809 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
28810 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
28811 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
28812 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
28813 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
28814 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
28815 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
28816 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
28817 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
28818 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
28819 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
28820 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
28821 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
28822 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
28823 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
28824 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
28825 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
28826 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
28827 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
28828 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
28829 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
28830 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
28831 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
28832 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
28833 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
28834 #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
28835 #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
28836 #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
28837 #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
28838
28839 typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
28840 #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
28841 #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
28842 #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
28843 #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
28844 #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
28845 #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
28846
28847 typedef pa_sample_format_t ma_pa_sample_format_t;
28848 #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
28849 #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
28850 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
28851 #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
28852 #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
28853 #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
28854 #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
28855 #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
28856 #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
28857 #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
28858 #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
28859 #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
28860 #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
28861 #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
28862
28863 typedef pa_mainloop ma_pa_mainloop;
28864 typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
28865 typedef pa_mainloop_api ma_pa_mainloop_api;
28866 typedef pa_context ma_pa_context;
28867 typedef pa_operation ma_pa_operation;
28868 typedef pa_stream ma_pa_stream;
28869 typedef pa_spawn_api ma_pa_spawn_api;
28870 typedef pa_buffer_attr ma_pa_buffer_attr;
28871 typedef pa_channel_map ma_pa_channel_map;
28872 typedef pa_cvolume ma_pa_cvolume;
28873 typedef pa_sample_spec ma_pa_sample_spec;
28874 typedef pa_sink_info ma_pa_sink_info;
28875 typedef pa_source_info ma_pa_source_info;
28876
28877 typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
28878 typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
28879 typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
28880 typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
28881 typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
28882 typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t;
28883 typedef pa_free_cb_t ma_pa_free_cb_t;
28884 #else
28885 #define MA_PA_OK 0
28886 #define MA_PA_ERR_ACCESS 1
28887 #define MA_PA_ERR_INVALID 2
28888 #define MA_PA_ERR_NOENTITY 5
28889 #define MA_PA_ERR_NOTSUPPORTED 19
28890
28891 #define MA_PA_CHANNELS_MAX 32
28892 #define MA_PA_RATE_MAX 384000
28893
28894 typedef int ma_pa_context_flags_t;
28895 #define MA_PA_CONTEXT_NOFLAGS 0x00000000
28896 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
28897 #define MA_PA_CONTEXT_NOFAIL 0x00000002
28898
28899 typedef int ma_pa_stream_flags_t;
28900 #define MA_PA_STREAM_NOFLAGS 0x00000000
28901 #define MA_PA_STREAM_START_CORKED 0x00000001
28902 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
28903 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
28904 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
28905 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
28906 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
28907 #define MA_PA_STREAM_FIX_FORMAT 0x00000040
28908 #define MA_PA_STREAM_FIX_RATE 0x00000080
28909 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100
28910 #define MA_PA_STREAM_DONT_MOVE 0x00000200
28911 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400
28912 #define MA_PA_STREAM_PEAK_DETECT 0x00000800
28913 #define MA_PA_STREAM_START_MUTED 0x00001000
28914 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
28915 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
28916 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
28917 #define MA_PA_STREAM_START_UNMUTED 0x00010000
28918 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
28919 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
28920 #define MA_PA_STREAM_PASSTHROUGH 0x00080000
28921
28922 typedef int ma_pa_sink_flags_t;
28923 #define MA_PA_SINK_NOFLAGS 0x00000000
28924 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
28925 #define MA_PA_SINK_LATENCY 0x00000002
28926 #define MA_PA_SINK_HARDWARE 0x00000004
28927 #define MA_PA_SINK_NETWORK 0x00000008
28928 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
28929 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
28930 #define MA_PA_SINK_FLAT_VOLUME 0x00000040
28931 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
28932 #define MA_PA_SINK_SET_FORMATS 0x00000100
28933
28934 typedef int ma_pa_source_flags_t;
28935 #define MA_PA_SOURCE_NOFLAGS 0x00000000
28936 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
28937 #define MA_PA_SOURCE_LATENCY 0x00000002
28938 #define MA_PA_SOURCE_HARDWARE 0x00000004
28939 #define MA_PA_SOURCE_NETWORK 0x00000008
28940 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
28941 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
28942 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
28943 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
28944
28945 typedef int ma_pa_context_state_t;
28946 #define MA_PA_CONTEXT_UNCONNECTED 0
28947 #define MA_PA_CONTEXT_CONNECTING 1
28948 #define MA_PA_CONTEXT_AUTHORIZING 2
28949 #define MA_PA_CONTEXT_SETTING_NAME 3
28950 #define MA_PA_CONTEXT_READY 4
28951 #define MA_PA_CONTEXT_FAILED 5
28952 #define MA_PA_CONTEXT_TERMINATED 6
28953
28954 typedef int ma_pa_stream_state_t;
28955 #define MA_PA_STREAM_UNCONNECTED 0
28956 #define MA_PA_STREAM_CREATING 1
28957 #define MA_PA_STREAM_READY 2
28958 #define MA_PA_STREAM_FAILED 3
28959 #define MA_PA_STREAM_TERMINATED 4
28960
28961 typedef int ma_pa_operation_state_t;
28962 #define MA_PA_OPERATION_RUNNING 0
28963 #define MA_PA_OPERATION_DONE 1
28964 #define MA_PA_OPERATION_CANCELLED 2
28965
28966 typedef int ma_pa_sink_state_t;
28967 #define MA_PA_SINK_INVALID_STATE -1
28968 #define MA_PA_SINK_RUNNING 0
28969 #define MA_PA_SINK_IDLE 1
28970 #define MA_PA_SINK_SUSPENDED 2
28971
28972 typedef int ma_pa_source_state_t;
28973 #define MA_PA_SOURCE_INVALID_STATE -1
28974 #define MA_PA_SOURCE_RUNNING 0
28975 #define MA_PA_SOURCE_IDLE 1
28976 #define MA_PA_SOURCE_SUSPENDED 2
28977
28978 typedef int ma_pa_seek_mode_t;
28979 #define MA_PA_SEEK_RELATIVE 0
28980 #define MA_PA_SEEK_ABSOLUTE 1
28981 #define MA_PA_SEEK_RELATIVE_ON_READ 2
28982 #define MA_PA_SEEK_RELATIVE_END 3
28983
28984 typedef int ma_pa_channel_position_t;
28985 #define MA_PA_CHANNEL_POSITION_INVALID -1
28986 #define MA_PA_CHANNEL_POSITION_MONO 0
28987 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
28988 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
28989 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
28990 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
28991 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
28992 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
28993 #define MA_PA_CHANNEL_POSITION_LFE 7
28994 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
28995 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
28996 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
28997 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
28998 #define MA_PA_CHANNEL_POSITION_AUX0 12
28999 #define MA_PA_CHANNEL_POSITION_AUX1 13
29000 #define MA_PA_CHANNEL_POSITION_AUX2 14
29001 #define MA_PA_CHANNEL_POSITION_AUX3 15
29002 #define MA_PA_CHANNEL_POSITION_AUX4 16
29003 #define MA_PA_CHANNEL_POSITION_AUX5 17
29004 #define MA_PA_CHANNEL_POSITION_AUX6 18
29005 #define MA_PA_CHANNEL_POSITION_AUX7 19
29006 #define MA_PA_CHANNEL_POSITION_AUX8 20
29007 #define MA_PA_CHANNEL_POSITION_AUX9 21
29008 #define MA_PA_CHANNEL_POSITION_AUX10 22
29009 #define MA_PA_CHANNEL_POSITION_AUX11 23
29010 #define MA_PA_CHANNEL_POSITION_AUX12 24
29011 #define MA_PA_CHANNEL_POSITION_AUX13 25
29012 #define MA_PA_CHANNEL_POSITION_AUX14 26
29013 #define MA_PA_CHANNEL_POSITION_AUX15 27
29014 #define MA_PA_CHANNEL_POSITION_AUX16 28
29015 #define MA_PA_CHANNEL_POSITION_AUX17 29
29016 #define MA_PA_CHANNEL_POSITION_AUX18 30
29017 #define MA_PA_CHANNEL_POSITION_AUX19 31
29018 #define MA_PA_CHANNEL_POSITION_AUX20 32
29019 #define MA_PA_CHANNEL_POSITION_AUX21 33
29020 #define MA_PA_CHANNEL_POSITION_AUX22 34
29021 #define MA_PA_CHANNEL_POSITION_AUX23 35
29022 #define MA_PA_CHANNEL_POSITION_AUX24 36
29023 #define MA_PA_CHANNEL_POSITION_AUX25 37
29024 #define MA_PA_CHANNEL_POSITION_AUX26 38
29025 #define MA_PA_CHANNEL_POSITION_AUX27 39
29026 #define MA_PA_CHANNEL_POSITION_AUX28 40
29027 #define MA_PA_CHANNEL_POSITION_AUX29 41
29028 #define MA_PA_CHANNEL_POSITION_AUX30 42
29029 #define MA_PA_CHANNEL_POSITION_AUX31 43
29030 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
29031 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
29032 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
29033 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
29034 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
29035 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
29036 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
29037 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
29038 #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
29039 #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
29040 #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
29041
29042 typedef int ma_pa_channel_map_def_t;
29043 #define MA_PA_CHANNEL_MAP_AIFF 0
29044 #define MA_PA_CHANNEL_MAP_ALSA 1
29045 #define MA_PA_CHANNEL_MAP_AUX 2
29046 #define MA_PA_CHANNEL_MAP_WAVEEX 3
29047 #define MA_PA_CHANNEL_MAP_OSS 4
29048 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
29049
29050 typedef int ma_pa_sample_format_t;
29051 #define MA_PA_SAMPLE_INVALID -1
29052 #define MA_PA_SAMPLE_U8 0
29053 #define MA_PA_SAMPLE_ALAW 1
29054 #define MA_PA_SAMPLE_ULAW 2
29055 #define MA_PA_SAMPLE_S16LE 3
29056 #define MA_PA_SAMPLE_S16BE 4
29057 #define MA_PA_SAMPLE_FLOAT32LE 5
29058 #define MA_PA_SAMPLE_FLOAT32BE 6
29059 #define MA_PA_SAMPLE_S32LE 7
29060 #define MA_PA_SAMPLE_S32BE 8
29061 #define MA_PA_SAMPLE_S24LE 9
29062 #define MA_PA_SAMPLE_S24BE 10
29063 #define MA_PA_SAMPLE_S24_32LE 11
29064 #define MA_PA_SAMPLE_S24_32BE 12
29065
29066 typedef struct ma_pa_mainloop ma_pa_mainloop;
29067 typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
29068 typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
29069 typedef struct ma_pa_context ma_pa_context;
29070 typedef struct ma_pa_operation ma_pa_operation;
29071 typedef struct ma_pa_stream ma_pa_stream;
29072 typedef struct ma_pa_spawn_api ma_pa_spawn_api;
29073
29074 typedef struct
29075 {
29076 ma_uint32 maxlength;
29077 ma_uint32 tlength;
29078 ma_uint32 prebuf;
29079 ma_uint32 minreq;
29080 ma_uint32 fragsize;
29081 } ma_pa_buffer_attr;
29082
29083 typedef struct
29084 {
29085 ma_uint8 channels;
29086 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
29087 } ma_pa_channel_map;
29088
29089 typedef struct
29090 {
29091 ma_uint8 channels;
29092 ma_uint32 values[MA_PA_CHANNELS_MAX];
29093 } ma_pa_cvolume;
29094
29095 typedef struct
29096 {
29097 ma_pa_sample_format_t format;
29098 ma_uint32 rate;
29099 ma_uint8 channels;
29100 } ma_pa_sample_spec;
29101
29102 typedef struct
29103 {
29104 const char* name;
29105 ma_uint32 index;
29106 const char* description;
29107 ma_pa_sample_spec sample_spec;
29108 ma_pa_channel_map channel_map;
29109 ma_uint32 owner_module;
29110 ma_pa_cvolume volume;
29111 int mute;
29112 ma_uint32 monitor_source;
29113 const char* monitor_source_name;
29114 ma_uint64 latency;
29115 const char* driver;
29116 ma_pa_sink_flags_t flags;
29117 void* proplist;
29118 ma_uint64 configured_latency;
29119 ma_uint32 base_volume;
29120 ma_pa_sink_state_t state;
29121 ma_uint32 n_volume_steps;
29122 ma_uint32 card;
29123 ma_uint32 n_ports;
29124 void** ports;
29125 void* active_port;
29126 ma_uint8 n_formats;
29127 void** formats;
29128 } ma_pa_sink_info;
29129
29130 typedef struct
29131 {
29132 const char *name;
29133 ma_uint32 index;
29134 const char *description;
29135 ma_pa_sample_spec sample_spec;
29136 ma_pa_channel_map channel_map;
29137 ma_uint32 owner_module;
29138 ma_pa_cvolume volume;
29139 int mute;
29140 ma_uint32 monitor_of_sink;
29141 const char *monitor_of_sink_name;
29142 ma_uint64 latency;
29143 const char *driver;
29144 ma_pa_source_flags_t flags;
29145 void* proplist;
29146 ma_uint64 configured_latency;
29147 ma_uint32 base_volume;
29148 ma_pa_source_state_t state;
29149 ma_uint32 n_volume_steps;
29150 ma_uint32 card;
29151 ma_uint32 n_ports;
29152 void** ports;
29153 void* active_port;
29154 ma_uint8 n_formats;
29155 void** formats;
29156 } ma_pa_source_info;
29157
29158 typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
29159 typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
29160 typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
29161 typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
29162 typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
29163 typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);
29164 typedef void (* ma_pa_free_cb_t) (void* p);
29165 #endif
29166
29167
29168 typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
29169 typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
29170 typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
29171 typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
29172 typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
29173 typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
29174 typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
29175 typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
29176 typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
29177 typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
29178 typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
29179 typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
29180 typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
29181 typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
29182 typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
29183 typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m);
29184 typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
29185 typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
29186 typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
29187 typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
29188 typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
29189 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);
29190 typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
29191 typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
29192 typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
29193 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);
29194 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);
29195 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);
29196 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);
29197 typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
29198 typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
29199 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);
29200 typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
29201 typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
29202 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);
29203 typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
29204 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);
29205 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);
29206 typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
29207 typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
29208 typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
29209 typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
29210 typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
29211 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);
29212 typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
29213 typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
29214 typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
29215 typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
29216 typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
29217 typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
29218 typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
29219 typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
29220 typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
29221 typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
29222 typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
29223 typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
29224 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);
29225 typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
29226 typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
29227 typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
29228 typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
29229
29230 typedef struct
29231 {
29232 ma_uint32 count;
29233 ma_uint32 capacity;
29234 ma_device_info* pInfo;
29235 } ma_pulse_device_enum_data;
29236
29237 static ma_result ma_result_from_pulse(int result)
29238 {
29239 if (result < 0) {
29240 return MA_ERROR;
29241 }
29242
29243 switch (result) {
29244 case MA_PA_OK: return MA_SUCCESS;
29245 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
29246 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
29247 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
29248 default: return MA_ERROR;
29249 }
29250 }
29251
29252 #if 0
29253 static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
29254 {
29255 if (ma_is_little_endian()) {
29256 switch (format) {
29257 case ma_format_s16: return MA_PA_SAMPLE_S16LE;
29258 case ma_format_s24: return MA_PA_SAMPLE_S24LE;
29259 case ma_format_s32: return MA_PA_SAMPLE_S32LE;
29260 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
29261 default: break;
29262 }
29263 } else {
29264 switch (format) {
29265 case ma_format_s16: return MA_PA_SAMPLE_S16BE;
29266 case ma_format_s24: return MA_PA_SAMPLE_S24BE;
29267 case ma_format_s32: return MA_PA_SAMPLE_S32BE;
29268 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
29269 default: break;
29270 }
29271 }
29272
29273 /* Endian agnostic. */
29274 switch (format) {
29275 case ma_format_u8: return MA_PA_SAMPLE_U8;
29276 default: return MA_PA_SAMPLE_INVALID;
29277 }
29278 }
29279 #endif
29280
29281 static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
29282 {
29283 if (ma_is_little_endian()) {
29284 switch (format) {
29285 case MA_PA_SAMPLE_S16LE: return ma_format_s16;
29286 case MA_PA_SAMPLE_S24LE: return ma_format_s24;
29287 case MA_PA_SAMPLE_S32LE: return ma_format_s32;
29288 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
29289 default: break;
29290 }
29291 } else {
29292 switch (format) {
29293 case MA_PA_SAMPLE_S16BE: return ma_format_s16;
29294 case MA_PA_SAMPLE_S24BE: return ma_format_s24;
29295 case MA_PA_SAMPLE_S32BE: return ma_format_s32;
29296 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
29297 default: break;
29298 }
29299 }
29300
29301 /* Endian agnostic. */
29302 switch (format) {
29303 case MA_PA_SAMPLE_U8: return ma_format_u8;
29304 default: return ma_format_unknown;
29305 }
29306 }
29307
29308 static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
29309 {
29310 switch (position)
29311 {
29312 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
29313 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
29314 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
29315 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
29316 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
29317 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
29318 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
29319 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
29320 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
29321 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
29322 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
29323 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
29324 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
29325 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
29326 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
29327 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
29328 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
29329 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
29330 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
29331 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
29332 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
29333 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
29334 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
29335 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
29336 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
29337 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
29338 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
29339 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
29340 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
29341 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
29342 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
29343 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
29344 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
29345 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
29346 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
29347 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
29348 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
29349 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
29350 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
29351 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
29352 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
29353 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
29354 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
29355 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
29356 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
29357 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
29358 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
29359 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
29360 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
29361 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
29362 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
29363 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
29364 default: return MA_CHANNEL_NONE;
29365 }
29366 }
29367
29368 #if 0
29369 static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
29370 {
29371 switch (position)
29372 {
29373 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
29374 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
29375 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
29376 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
29377 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
29378 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
29379 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
29380 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
29381 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
29382 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
29383 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
29384 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
29385 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
29386 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
29387 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
29388 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
29389 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
29390 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
29391 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
29392 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
29393 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
29394 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
29395 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
29396 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
29397 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
29398 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
29399 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
29400 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
29401 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
29402 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
29403 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
29404 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
29405 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
29406 default: return (ma_pa_channel_position_t)position;
29407 }
29408 }
29409 #endif
29410
29411 static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
29412 {
29413 int resultPA;
29414 ma_pa_operation_state_t state;
29415
29416 MA_ASSERT(pContext != NULL);
29417 MA_ASSERT(pOP != NULL);
29418
29419 for (;;) {
29420 state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);
29421 if (state != MA_PA_OPERATION_RUNNING) {
29422 break; /* Done. */
29423 }
29424
29425 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
29426 if (resultPA < 0) {
29427 return ma_result_from_pulse(resultPA);
29428 }
29429 }
29430
29431 return MA_SUCCESS;
29432 }
29433
29434 static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
29435 {
29436 ma_result result;
29437
29438 if (pOP == NULL) {
29439 return MA_INVALID_ARGS;
29440 }
29441
29442 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
29443 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
29444
29445 return result;
29446 }
29447
29448 static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext)
29449 {
29450 int resultPA;
29451 ma_pa_context_state_t state;
29452
29453 for (;;) {
29454 state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext);
29455 if (state == MA_PA_CONTEXT_READY) {
29456 break; /* Done. */
29457 }
29458
29459 if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
29460 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.");
29461 return MA_ERROR;
29462 }
29463
29464 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
29465 if (resultPA < 0) {
29466 return ma_result_from_pulse(resultPA);
29467 }
29468 }
29469
29470 /* Should never get here. */
29471 return MA_SUCCESS;
29472 }
29473
29474 static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream)
29475 {
29476 int resultPA;
29477 ma_pa_stream_state_t state;
29478
29479 for (;;) {
29480 state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream);
29481 if (state == MA_PA_STREAM_READY) {
29482 break; /* Done. */
29483 }
29484
29485 if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {
29486 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.");
29487 return MA_ERROR;
29488 }
29489
29490 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
29491 if (resultPA < 0) {
29492 return ma_result_from_pulse(resultPA);
29493 }
29494 }
29495
29496 return MA_SUCCESS;
29497 }
29498
29499
29500 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)
29501 {
29502 ma_result result;
29503 ma_ptr pMainLoop;
29504 ma_ptr pPulseContext;
29505
29506 MA_ASSERT(ppMainLoop != NULL);
29507 MA_ASSERT(ppPulseContext != NULL);
29508
29509 /* 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. */
29510 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
29511 if (pMainLoop == NULL) {
29512 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.");
29513 return MA_FAILED_TO_INIT_BACKEND;
29514 }
29515
29516 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);
29517 if (pPulseContext == NULL) {
29518 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.");
29519 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
29520 return MA_FAILED_TO_INIT_BACKEND;
29521 }
29522
29523 /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */
29524 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));
29525 if (result != MA_SUCCESS) {
29526 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.");
29527 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
29528 return result;
29529 }
29530
29531 /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */
29532 result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext);
29533 if (result != MA_SUCCESS) {
29534 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed.");
29535 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
29536 return result;
29537 }
29538
29539 *ppMainLoop = pMainLoop;
29540 *ppPulseContext = pPulseContext;
29541
29542 return MA_SUCCESS;
29543 }
29544
29545
29546 static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
29547 {
29548 ma_pa_sink_info* pInfoOut;
29549
29550 if (endOfList > 0) {
29551 return;
29552 }
29553
29554 /*
29555 There has been a report that indicates that pInfo can be null which results
29556 in a null pointer dereference below. We'll check for this for safety.
29557 */
29558 if (pInfo == NULL) {
29559 return;
29560 }
29561
29562 pInfoOut = (ma_pa_sink_info*)pUserData;
29563 MA_ASSERT(pInfoOut != NULL);
29564
29565 *pInfoOut = *pInfo;
29566
29567 (void)pPulseContext; /* Unused. */
29568 }
29569
29570 static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
29571 {
29572 ma_pa_source_info* pInfoOut;
29573
29574 if (endOfList > 0) {
29575 return;
29576 }
29577
29578 /*
29579 There has been a report that indicates that pInfo can be null which results
29580 in a null pointer dereference below. We'll check for this for safety.
29581 */
29582 if (pInfo == NULL) {
29583 return;
29584 }
29585
29586 pInfoOut = (ma_pa_source_info*)pUserData;
29587 MA_ASSERT(pInfoOut != NULL);
29588
29589 *pInfoOut = *pInfo;
29590
29591 (void)pPulseContext; /* Unused. */
29592 }
29593
29594 #if 0
29595 static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
29596 {
29597 ma_device* pDevice;
29598
29599 if (endOfList > 0) {
29600 return;
29601 }
29602
29603 pDevice = (ma_device*)pUserData;
29604 MA_ASSERT(pDevice != NULL);
29605
29606 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
29607
29608 (void)pPulseContext; /* Unused. */
29609 }
29610
29611 static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
29612 {
29613 ma_device* pDevice;
29614
29615 if (endOfList > 0) {
29616 return;
29617 }
29618
29619 pDevice = (ma_device*)pUserData;
29620 MA_ASSERT(pDevice != NULL);
29621
29622 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
29623
29624 (void)pPulseContext; /* Unused. */
29625 }
29626 #endif
29627
29628 static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
29629 {
29630 ma_pa_operation* pOP;
29631
29632 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);
29633 if (pOP == NULL) {
29634 return MA_ERROR;
29635 }
29636
29637 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29638 }
29639
29640 static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
29641 {
29642 ma_pa_operation* pOP;
29643
29644 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);
29645 if (pOP == NULL) {
29646 return MA_ERROR;
29647 }
29648
29649 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29650 }
29651
29652 static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
29653 {
29654 ma_result result;
29655
29656 MA_ASSERT(pContext != NULL);
29657 MA_ASSERT(pIndex != NULL);
29658
29659 if (pIndex != NULL) {
29660 *pIndex = (ma_uint32)-1;
29661 }
29662
29663 if (deviceType == ma_device_type_playback) {
29664 ma_pa_sink_info sinkInfo;
29665 result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);
29666 if (result != MA_SUCCESS) {
29667 return result;
29668 }
29669
29670 if (pIndex != NULL) {
29671 *pIndex = sinkInfo.index;
29672 }
29673 }
29674
29675 if (deviceType == ma_device_type_capture) {
29676 ma_pa_source_info sourceInfo;
29677 result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);
29678 if (result != MA_SUCCESS) {
29679 return result;
29680 }
29681
29682 if (pIndex != NULL) {
29683 *pIndex = sourceInfo.index;
29684 }
29685 }
29686
29687 return MA_SUCCESS;
29688 }
29689
29690
29691 typedef struct
29692 {
29693 ma_context* pContext;
29694 ma_enum_devices_callback_proc callback;
29695 void* pUserData;
29696 ma_bool32 isTerminated;
29697 ma_uint32 defaultDeviceIndexPlayback;
29698 ma_uint32 defaultDeviceIndexCapture;
29699 } ma_context_enumerate_devices_callback_data__pulse;
29700
29701 static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
29702 {
29703 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
29704 ma_device_info deviceInfo;
29705
29706 MA_ASSERT(pData != NULL);
29707
29708 if (endOfList || pData->isTerminated) {
29709 return;
29710 }
29711
29712 MA_ZERO_OBJECT(&deviceInfo);
29713
29714 /* The name from PulseAudio is the ID for miniaudio. */
29715 if (pSinkInfo->name != NULL) {
29716 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
29717 }
29718
29719 /* The description from PulseAudio is the name for miniaudio. */
29720 if (pSinkInfo->description != NULL) {
29721 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
29722 }
29723
29724 if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {
29725 deviceInfo.isDefault = MA_TRUE;
29726 }
29727
29728 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
29729
29730 (void)pPulseContext; /* Unused. */
29731 }
29732
29733 static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)
29734 {
29735 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
29736 ma_device_info deviceInfo;
29737
29738 MA_ASSERT(pData != NULL);
29739
29740 if (endOfList || pData->isTerminated) {
29741 return;
29742 }
29743
29744 MA_ZERO_OBJECT(&deviceInfo);
29745
29746 /* The name from PulseAudio is the ID for miniaudio. */
29747 if (pSourceInfo->name != NULL) {
29748 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);
29749 }
29750
29751 /* The description from PulseAudio is the name for miniaudio. */
29752 if (pSourceInfo->description != NULL) {
29753 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);
29754 }
29755
29756 if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {
29757 deviceInfo.isDefault = MA_TRUE;
29758 }
29759
29760 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
29761
29762 (void)pPulseContext; /* Unused. */
29763 }
29764
29765 static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
29766 {
29767 ma_result result = MA_SUCCESS;
29768 ma_context_enumerate_devices_callback_data__pulse callbackData;
29769 ma_pa_operation* pOP = NULL;
29770
29771 MA_ASSERT(pContext != NULL);
29772 MA_ASSERT(callback != NULL);
29773
29774 callbackData.pContext = pContext;
29775 callbackData.callback = callback;
29776 callbackData.pUserData = pUserData;
29777 callbackData.isTerminated = MA_FALSE;
29778 callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;
29779 callbackData.defaultDeviceIndexCapture = (ma_uint32)-1;
29780
29781 /* We need to get the index of the default devices. */
29782 ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);
29783 ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture);
29784
29785 /* Playback. */
29786 if (!callbackData.isTerminated) {
29787 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);
29788 if (pOP == NULL) {
29789 result = MA_ERROR;
29790 goto done;
29791 }
29792
29793 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29794 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
29795
29796 if (result != MA_SUCCESS) {
29797 goto done;
29798 }
29799 }
29800
29801
29802 /* Capture. */
29803 if (!callbackData.isTerminated) {
29804 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);
29805 if (pOP == NULL) {
29806 result = MA_ERROR;
29807 goto done;
29808 }
29809
29810 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29811 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
29812
29813 if (result != MA_SUCCESS) {
29814 goto done;
29815 }
29816 }
29817
29818 done:
29819 return result;
29820 }
29821
29822
29823 typedef struct
29824 {
29825 ma_device_info* pDeviceInfo;
29826 ma_uint32 defaultDeviceIndex;
29827 ma_bool32 foundDevice;
29828 } ma_context_get_device_info_callback_data__pulse;
29829
29830 static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
29831 {
29832 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
29833
29834 if (endOfList > 0) {
29835 return;
29836 }
29837
29838 MA_ASSERT(pData != NULL);
29839 pData->foundDevice = MA_TRUE;
29840
29841 if (pInfo->name != NULL) {
29842 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
29843 }
29844
29845 if (pInfo->description != NULL) {
29846 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
29847 }
29848
29849 /*
29850 We're just reporting a single data format here. I think technically PulseAudio might support
29851 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
29852 report the "native" device format.
29853 */
29854 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
29855 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
29856 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
29857 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
29858 pData->pDeviceInfo->nativeDataFormatCount = 1;
29859
29860 if (pData->defaultDeviceIndex == pInfo->index) {
29861 pData->pDeviceInfo->isDefault = MA_TRUE;
29862 }
29863
29864 (void)pPulseContext; /* Unused. */
29865 }
29866
29867 static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
29868 {
29869 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
29870
29871 if (endOfList > 0) {
29872 return;
29873 }
29874
29875 MA_ASSERT(pData != NULL);
29876 pData->foundDevice = MA_TRUE;
29877
29878 if (pInfo->name != NULL) {
29879 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
29880 }
29881
29882 if (pInfo->description != NULL) {
29883 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
29884 }
29885
29886 /*
29887 We're just reporting a single data format here. I think technically PulseAudio might support
29888 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
29889 report the "native" device format.
29890 */
29891 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
29892 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
29893 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
29894 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
29895 pData->pDeviceInfo->nativeDataFormatCount = 1;
29896
29897 if (pData->defaultDeviceIndex == pInfo->index) {
29898 pData->pDeviceInfo->isDefault = MA_TRUE;
29899 }
29900
29901 (void)pPulseContext; /* Unused. */
29902 }
29903
29904 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)
29905 {
29906 ma_result result = MA_SUCCESS;
29907 ma_context_get_device_info_callback_data__pulse callbackData;
29908 ma_pa_operation* pOP = NULL;
29909 const char* pDeviceName = NULL;
29910
29911 MA_ASSERT(pContext != NULL);
29912
29913 callbackData.pDeviceInfo = pDeviceInfo;
29914 callbackData.foundDevice = MA_FALSE;
29915
29916 if (pDeviceID != NULL) {
29917 pDeviceName = pDeviceID->pulse;
29918 } else {
29919 pDeviceName = NULL;
29920 }
29921
29922 result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);
29923
29924 if (deviceType == ma_device_type_playback) {
29925 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);
29926 } else {
29927 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);
29928 }
29929
29930 if (pOP != NULL) {
29931 ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
29932 } else {
29933 result = MA_ERROR;
29934 goto done;
29935 }
29936
29937 if (!callbackData.foundDevice) {
29938 result = MA_NO_DEVICE;
29939 goto done;
29940 }
29941
29942 done:
29943 return result;
29944 }
29945
29946 static ma_result ma_device_uninit__pulse(ma_device* pDevice)
29947 {
29948 ma_context* pContext;
29949
29950 MA_ASSERT(pDevice != NULL);
29951
29952 pContext = pDevice->pContext;
29953 MA_ASSERT(pContext != NULL);
29954
29955 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29956 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
29957 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
29958 }
29959
29960 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29961 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29962 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
29963 }
29964
29965 if (pDevice->type == ma_device_type_duplex) {
29966 ma_duplex_rb_uninit(&pDevice->duplexRB);
29967 }
29968
29969 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
29970 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
29971 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
29972
29973 return MA_SUCCESS;
29974 }
29975
29976 static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
29977 {
29978 ma_pa_buffer_attr attr;
29979 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
29980 attr.tlength = attr.maxlength / periods;
29981 attr.prebuf = (ma_uint32)-1;
29982 attr.minreq = (ma_uint32)-1;
29983 attr.fragsize = attr.maxlength / periods;
29984
29985 return attr;
29986 }
29987
29988 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)
29989 {
29990 static int g_StreamCounter = 0;
29991 char actualStreamName[256];
29992
29993 if (pStreamName != NULL) {
29994 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
29995 } else {
29996 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
29997 ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
29998 }
29999 g_StreamCounter += 1;
30000
30001 return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
30002 }
30003
30004
30005 static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
30006 {
30007 ma_device* pDevice = (ma_device*)pUserData;
30008 ma_uint32 bpf;
30009 ma_uint32 deviceState;
30010 ma_uint64 frameCount;
30011 ma_uint64 framesProcessed;
30012
30013 MA_ASSERT(pDevice != NULL);
30014
30015 /*
30016 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
30017 can fire this callback before the stream has even started. Ridiculous.
30018 */
30019 deviceState = ma_device_get_state(pDevice);
30020 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
30021 return;
30022 }
30023
30024 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
30025 MA_ASSERT(bpf > 0);
30026
30027 frameCount = byteCount / bpf;
30028 framesProcessed = 0;
30029
30030 while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) {
30031 const void* pMappedPCMFrames;
30032 size_t bytesMapped;
30033 ma_uint64 framesMapped;
30034
30035 int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);
30036 if (pulseResult < 0) {
30037 break; /* Failed to map. Abort. */
30038 }
30039
30040 framesMapped = bytesMapped / bpf;
30041 if (framesMapped > 0) {
30042 if (pMappedPCMFrames != NULL) {
30043 ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);
30044 } else {
30045 /* It's a hole. */
30046 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n");
30047 }
30048
30049 pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);
30050 if (pulseResult < 0) {
30051 break; /* Failed to drop the buffer. */
30052 }
30053
30054 framesProcessed += framesMapped;
30055
30056 } else {
30057 /* Nothing was mapped. Just abort. */
30058 break;
30059 }
30060 }
30061 }
30062
30063 static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)
30064 {
30065 ma_result result = MA_SUCCESS;
30066 ma_uint64 framesProcessed = 0;
30067 size_t bytesMapped;
30068 ma_uint32 bpf;
30069 ma_uint32 deviceState;
30070
30071 MA_ASSERT(pDevice != NULL);
30072 MA_ASSERT(pStream != NULL);
30073
30074 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
30075 MA_ASSERT(bpf > 0);
30076
30077 deviceState = ma_device_get_state(pDevice);
30078
30079 bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);
30080 if (bytesMapped != (size_t)-1) {
30081 if (bytesMapped > 0) {
30082 ma_uint64 framesMapped;
30083 void* pMappedPCMFrames;
30084 int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);
30085 if (pulseResult < 0) {
30086 result = ma_result_from_pulse(pulseResult);
30087 goto done;
30088 }
30089
30090 framesMapped = bytesMapped / bpf;
30091
30092 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. */
30093 ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);
30094 } else {
30095 /* Device is not started. Write silence. */
30096 ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels);
30097 }
30098
30099 pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);
30100 if (pulseResult < 0) {
30101 result = ma_result_from_pulse(pulseResult);
30102 goto done; /* Failed to write data to stream. */
30103 }
30104
30105 framesProcessed += framesMapped;
30106 } else {
30107 result = MA_SUCCESS; /* No data available for writing. */
30108 goto done;
30109 }
30110 } else {
30111 result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */
30112 goto done;
30113 }
30114
30115 done:
30116 if (pFramesProcessed != NULL) {
30117 *pFramesProcessed = framesProcessed;
30118 }
30119
30120 return result;
30121 }
30122
30123 static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
30124 {
30125 ma_device* pDevice = (ma_device*)pUserData;
30126 ma_uint32 bpf;
30127 ma_uint64 frameCount;
30128 ma_uint64 framesProcessed;
30129 ma_uint32 deviceState;
30130 ma_result result;
30131
30132 MA_ASSERT(pDevice != NULL);
30133
30134 /*
30135 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
30136 can fire this callback before the stream has even started. Ridiculous.
30137 */
30138 deviceState = ma_device_get_state(pDevice);
30139 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
30140 return;
30141 }
30142
30143 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
30144 MA_ASSERT(bpf > 0);
30145
30146 frameCount = byteCount / bpf;
30147 framesProcessed = 0;
30148
30149 while (framesProcessed < frameCount) {
30150 ma_uint64 framesProcessedThisIteration;
30151
30152 /* Don't keep trying to process frames if the device isn't started. */
30153 deviceState = ma_device_get_state(pDevice);
30154 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
30155 break;
30156 }
30157
30158 result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);
30159 if (result != MA_SUCCESS) {
30160 break;
30161 }
30162
30163 framesProcessed += framesProcessedThisIteration;
30164 }
30165 }
30166
30167 static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData)
30168 {
30169 ma_device* pDevice = (ma_device*)pUserData;
30170 int suspended;
30171
30172 (void)pStream;
30173
30174 suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream);
30175 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);
30176
30177 if (suspended < 0) {
30178 return;
30179 }
30180
30181 if (suspended == 1) {
30182 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n");
30183 ma_device__on_notification_stopped(pDevice);
30184 } else {
30185 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n");
30186 ma_device__on_notification_started(pDevice);
30187 }
30188 }
30189
30190 static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)
30191 {
30192 ma_device* pDevice = (ma_device*)pUserData;
30193
30194 (void)pStream;
30195 (void)pUserData;
30196
30197 ma_device__on_notification_rerouted(pDevice);
30198 }
30199
30200 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
30201 {
30202 /*
30203 There have been reports from users where buffers of < ~20ms result glitches when running through
30204 PipeWire. To work around this we're going to have to use a different default buffer size.
30205 */
30206 const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25;
30207 const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
30208
30209 MA_ASSERT(nativeSampleRate != 0);
30210
30211 if (pDescriptor->periodSizeInFrames == 0) {
30212 if (pDescriptor->periodSizeInMilliseconds == 0) {
30213 if (performanceProfile == ma_performance_profile_low_latency) {
30214 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate);
30215 } else {
30216 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate);
30217 }
30218 } else {
30219 return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
30220 }
30221 } else {
30222 return pDescriptor->periodSizeInFrames;
30223 }
30224 }
30225
30226 static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
30227 {
30228 /*
30229 Notes for PulseAudio:
30230
30231 - We're always using native format/channels/rate regardless of whether or not PulseAudio
30232 supports the format directly through their own data conversion system. I'm doing this to
30233 reduce as much variability from the PulseAudio side as possible because it's seems to be
30234 extremely unreliable at everything it does.
30235
30236 - When both the period size in frames and milliseconds are 0, we default to miniaudio's
30237 default buffer sizes rather than leaving it up to PulseAudio because I don't trust
30238 PulseAudio to give us any kind of reasonable latency by default.
30239
30240 - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this
30241 flag, capture mode will just not work properly until you open another PulseAudio app.
30242 */
30243
30244 ma_result result = MA_SUCCESS;
30245 int error = 0;
30246 const char* devPlayback = NULL;
30247 const char* devCapture = NULL;
30248 ma_format format = ma_format_unknown;
30249 ma_uint32 channels = 0;
30250 ma_uint32 sampleRate = 0;
30251 ma_pa_sink_info sinkInfo;
30252 ma_pa_source_info sourceInfo;
30253 ma_pa_sample_spec ss;
30254 ma_pa_channel_map cmap;
30255 ma_pa_buffer_attr attr;
30256 const ma_pa_sample_spec* pActualSS = NULL;
30257 const ma_pa_buffer_attr* pActualAttr = NULL;
30258 ma_uint32 iChannel;
30259 ma_pa_stream_flags_t streamFlags;
30260
30261 MA_ASSERT(pDevice != NULL);
30262 MA_ZERO_OBJECT(&pDevice->pulse);
30263
30264 if (pConfig->deviceType == ma_device_type_loopback) {
30265 return MA_DEVICE_TYPE_NOT_SUPPORTED;
30266 }
30267
30268 /* No exclusive mode with the PulseAudio backend. */
30269 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
30270 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
30271 return MA_SHARE_MODE_NOT_SUPPORTED;
30272 }
30273
30274 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30275 if (pDescriptorPlayback->pDeviceID != NULL) {
30276 devPlayback = pDescriptorPlayback->pDeviceID->pulse;
30277 }
30278
30279 format = pDescriptorPlayback->format;
30280 channels = pDescriptorPlayback->channels;
30281 sampleRate = pDescriptorPlayback->sampleRate;
30282 }
30283
30284 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30285 if (pDescriptorCapture->pDeviceID != NULL) {
30286 devCapture = pDescriptorCapture->pDeviceID->pulse;
30287 }
30288
30289 format = pDescriptorCapture->format;
30290 channels = pDescriptorCapture->channels;
30291 sampleRate = pDescriptorCapture->sampleRate;
30292 }
30293
30294
30295
30296 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);
30297 if (result != MA_SUCCESS) {
30298 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n");
30299 return result;
30300 }
30301
30302 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30303 result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
30304 if (result != MA_SUCCESS) {
30305 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.");
30306 goto on_error0;
30307 }
30308
30309 ss = sourceInfo.sample_spec;
30310 cmap = sourceInfo.channel_map;
30311
30312 /* Use the requested channel count if we have one. */
30313 if (pDescriptorCapture->channels != 0) {
30314 ss.channels = pDescriptorCapture->channels;
30315 }
30316
30317 /* Use a default channel map. */
30318 ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);
30319
30320 /* Use the requested sample rate if one was specified. */
30321 if (pDescriptorCapture->sampleRate != 0) {
30322 ss.rate = pDescriptorCapture->sampleRate;
30323 }
30324
30325 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
30326 if (ma_is_little_endian()) {
30327 ss.format = MA_PA_SAMPLE_FLOAT32LE;
30328 } else {
30329 ss.format = MA_PA_SAMPLE_FLOAT32BE;
30330 }
30331 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");
30332 }
30333 if (ss.rate == 0) {
30334 ss.rate = MA_DEFAULT_SAMPLE_RATE;
30335 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
30336 }
30337 if (ss.channels == 0) {
30338 ss.channels = MA_DEFAULT_CHANNELS;
30339 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
30340 }
30341
30342 /* We now have enough information to calculate our actual period size in frames. */
30343 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
30344
30345 attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
30346 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);
30347
30348 pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
30349 if (pDevice->pulse.pStreamCapture == NULL) {
30350 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n");
30351 result = MA_ERROR;
30352 goto on_error0;
30353 }
30354
30355
30356 /* The callback needs to be set before connecting the stream. */
30357 ((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);
30358
30359 /* State callback for checking when the device has been corked. */
30360 ((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);
30361
30362 /* Rerouting notification. */
30363 ((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);
30364
30365
30366 /* Connect after we've got all of our internal state set up. */
30367 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
30368 if (devCapture != NULL) {
30369 streamFlags |= MA_PA_STREAM_DONT_MOVE;
30370 }
30371
30372 error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
30373 if (error != MA_PA_OK) {
30374 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.");
30375 result = ma_result_from_pulse(error);
30376 goto on_error1;
30377 }
30378
30379 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
30380 if (result != MA_SUCCESS) {
30381 goto on_error2;
30382 }
30383
30384
30385 /* Internal format. */
30386 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30387 if (pActualSS != NULL) {
30388 ss = *pActualSS;
30389 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);
30390 } else {
30391 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n");
30392 }
30393
30394 pDescriptorCapture->format = ma_format_from_pulse(ss.format);
30395 pDescriptorCapture->channels = ss.channels;
30396 pDescriptorCapture->sampleRate = ss.rate;
30397
30398 if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) {
30399 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);
30400 result = MA_ERROR;
30401 goto on_error4;
30402 }
30403
30404 /* Internal channel map. */
30405
30406 /*
30407 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
30408 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
30409 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
30410 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
30411 fixed sooner than later. I might remove this hack later.
30412 */
30413 if (pDescriptorCapture->channels > 2) {
30414 for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {
30415 pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
30416 }
30417 } else {
30418 /* Hack for mono and stereo. */
30419 if (pDescriptorCapture->channels == 1) {
30420 pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO;
30421 } else if (pDescriptorCapture->channels == 2) {
30422 pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
30423 pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
30424 } else {
30425 MA_ASSERT(MA_FALSE); /* Should never hit this. */
30426 }
30427 }
30428
30429
30430 /* Buffer. */
30431 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30432 if (pActualAttr != NULL) {
30433 attr = *pActualAttr;
30434 }
30435
30436 if (attr.fragsize > 0) {
30437 pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);
30438 } else {
30439 pDescriptorCapture->periodCount = 1;
30440 }
30441
30442 pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
30443 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);
30444 }
30445
30446 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30447 result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);
30448 if (result != MA_SUCCESS) {
30449 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n");
30450 goto on_error2;
30451 }
30452
30453 ss = sinkInfo.sample_spec;
30454 cmap = sinkInfo.channel_map;
30455
30456 /* Use the requested channel count if we have one. */
30457 if (pDescriptorPlayback->channels != 0) {
30458 ss.channels = pDescriptorPlayback->channels;
30459 }
30460
30461 /* Use a default channel map. */
30462 ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);
30463
30464
30465 /* Use the requested sample rate if one was specified. */
30466 if (pDescriptorPlayback->sampleRate != 0) {
30467 ss.rate = pDescriptorPlayback->sampleRate;
30468 }
30469
30470 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
30471 if (ma_is_little_endian()) {
30472 ss.format = MA_PA_SAMPLE_FLOAT32LE;
30473 } else {
30474 ss.format = MA_PA_SAMPLE_FLOAT32BE;
30475 }
30476 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");
30477 }
30478 if (ss.rate == 0) {
30479 ss.rate = MA_DEFAULT_SAMPLE_RATE;
30480 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
30481 }
30482 if (ss.channels == 0) {
30483 ss.channels = MA_DEFAULT_CHANNELS;
30484 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
30485 }
30486
30487 /* We now have enough information to calculate the actual buffer size in frames. */
30488 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
30489
30490 attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
30491
30492 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);
30493
30494 pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
30495 if (pDevice->pulse.pStreamPlayback == NULL) {
30496 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n");
30497 result = MA_ERROR;
30498 goto on_error2;
30499 }
30500
30501
30502 /*
30503 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
30504 device state of ma_device_state_uninitialized.
30505 */
30506 ((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);
30507
30508 /* State callback for checking when the device has been corked. */
30509 ((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);
30510
30511 /* Rerouting notification. */
30512 ((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);
30513
30514
30515 /* Connect after we've got all of our internal state set up. */
30516 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
30517 if (devPlayback != NULL) {
30518 streamFlags |= MA_PA_STREAM_DONT_MOVE;
30519 }
30520
30521 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);
30522 if (error != MA_PA_OK) {
30523 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.");
30524 result = ma_result_from_pulse(error);
30525 goto on_error3;
30526 }
30527
30528 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30529 if (result != MA_SUCCESS) {
30530 goto on_error3;
30531 }
30532
30533
30534 /* Internal format. */
30535 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30536 if (pActualSS != NULL) {
30537 ss = *pActualSS;
30538 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);
30539 } else {
30540 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n");
30541 }
30542
30543 pDescriptorPlayback->format = ma_format_from_pulse(ss.format);
30544 pDescriptorPlayback->channels = ss.channels;
30545 pDescriptorPlayback->sampleRate = ss.rate;
30546
30547 if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) {
30548 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);
30549 result = MA_ERROR;
30550 goto on_error4;
30551 }
30552
30553 /* Internal channel map. */
30554
30555 /*
30556 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
30557 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
30558 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
30559 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
30560 fixed sooner than later. I might remove this hack later.
30561 */
30562 if (pDescriptorPlayback->channels > 2) {
30563 for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {
30564 pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
30565 }
30566 } else {
30567 /* Hack for mono and stereo. */
30568 if (pDescriptorPlayback->channels == 1) {
30569 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO;
30570 } else if (pDescriptorPlayback->channels == 2) {
30571 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
30572 pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
30573 } else {
30574 MA_ASSERT(MA_FALSE); /* Should never hit this. */
30575 }
30576 }
30577
30578
30579 /* Buffer. */
30580 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30581 if (pActualAttr != NULL) {
30582 attr = *pActualAttr;
30583 }
30584
30585 if (attr.tlength > 0) {
30586 pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1);
30587 } else {
30588 pDescriptorPlayback->periodCount = 1;
30589 }
30590
30591 pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;
30592 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);
30593 }
30594
30595
30596 /*
30597 We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main
30598 part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for
30599 us later on because that will only do it if it's a fully asynchronous backend - i.e. the
30600 onDeviceDataLoop callback is NULL, which is not the case for PulseAudio.
30601 */
30602 if (pConfig->deviceType == ma_device_type_duplex) {
30603 ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format;
30604 ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels;
30605 ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate;
30606
30607 result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
30608 if (result != MA_SUCCESS) {
30609 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result));
30610 goto on_error4;
30611 }
30612 }
30613
30614 return MA_SUCCESS;
30615
30616
30617 on_error4:
30618 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30619 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30620 }
30621 on_error3:
30622 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
30623 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
30624 }
30625 on_error2:
30626 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30627 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30628 }
30629 on_error1:
30630 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
30631 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
30632 }
30633 on_error0:
30634 return result;
30635 }
30636
30637
30638 static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
30639 {
30640 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
30641 MA_ASSERT(pIsSuccessful != NULL);
30642
30643 *pIsSuccessful = (ma_bool32)success;
30644
30645 (void)pStream; /* Unused. */
30646 }
30647
30648 static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
30649 {
30650 ma_context* pContext = pDevice->pContext;
30651 ma_bool32 wasSuccessful;
30652 ma_pa_stream* pStream;
30653 ma_pa_operation* pOP;
30654 ma_result result;
30655
30656 /* This should not be called with a duplex device type. */
30657 if (deviceType == ma_device_type_duplex) {
30658 return MA_INVALID_ARGS;
30659 }
30660
30661 wasSuccessful = MA_FALSE;
30662
30663 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
30664 MA_ASSERT(pStream != NULL);
30665
30666 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
30667 if (pOP == NULL) {
30668 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.");
30669 return MA_ERROR;
30670 }
30671
30672 result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
30673 if (result != MA_SUCCESS) {
30674 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.");
30675 return result;
30676 }
30677
30678 if (!wasSuccessful) {
30679 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start");
30680 return MA_ERROR;
30681 }
30682
30683 return MA_SUCCESS;
30684 }
30685
30686 static ma_result ma_device_start__pulse(ma_device* pDevice)
30687 {
30688 ma_result result;
30689
30690 MA_ASSERT(pDevice != NULL);
30691
30692 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30693 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
30694 if (result != MA_SUCCESS) {
30695 return result;
30696 }
30697 }
30698
30699 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30700 /*
30701 We need to fill some data before uncorking. Not doing this will result in the write callback
30702 never getting fired. We're not going to abort if writing fails because I still want the device
30703 to get uncorked.
30704 */
30705 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.*/
30706
30707 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
30708 if (result != MA_SUCCESS) {
30709 return result;
30710 }
30711 }
30712
30713 return MA_SUCCESS;
30714 }
30715
30716 static ma_result ma_device_stop__pulse(ma_device* pDevice)
30717 {
30718 ma_result result;
30719
30720 MA_ASSERT(pDevice != NULL);
30721
30722 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
30723 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
30724 if (result != MA_SUCCESS) {
30725 return result;
30726 }
30727 }
30728
30729 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
30730 /*
30731 Ideally we would drain the device here, but there's been cases where PulseAudio seems to be
30732 broken on some systems to the point where no audio processing seems to happen. When this
30733 happens, draining never completes and we get stuck here. For now I'm disabling draining of
30734 the device so we don't just freeze the application.
30735 */
30736 #if 0
30737 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);
30738 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
30739 #endif
30740
30741 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
30742 if (result != MA_SUCCESS) {
30743 return result;
30744 }
30745 }
30746
30747 return MA_SUCCESS;
30748 }
30749
30750 static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
30751 {
30752 int resultPA;
30753
30754 MA_ASSERT(pDevice != NULL);
30755
30756 /* NOTE: Don't start the device here. It'll be done at a higher level. */
30757
30758 /*
30759 All data is handled through callbacks. All we need to do is iterate over the main loop and let
30760 the callbacks deal with it.
30761 */
30762 while (ma_device_get_state(pDevice) == ma_device_state_started) {
30763 resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
30764 if (resultPA < 0) {
30765 break;
30766 }
30767 }
30768
30769 /* NOTE: Don't stop the device here. It'll be done at a higher level. */
30770 return MA_SUCCESS;
30771 }
30772
30773 static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
30774 {
30775 MA_ASSERT(pDevice != NULL);
30776
30777 ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
30778
30779 return MA_SUCCESS;
30780 }
30781
30782 static ma_result ma_context_uninit__pulse(ma_context* pContext)
30783 {
30784 MA_ASSERT(pContext != NULL);
30785 MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
30786
30787 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
30788 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
30789 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
30790
30791 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
30792 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
30793
30794 #ifndef MA_NO_RUNTIME_LINKING
30795 ma_dlclose(pContext, pContext->pulse.pulseSO);
30796 #endif
30797
30798 return MA_SUCCESS;
30799 }
30800
30801 static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
30802 {
30803 ma_result result;
30804 #ifndef MA_NO_RUNTIME_LINKING
30805 const char* libpulseNames[] = {
30806 "libpulse.so",
30807 "libpulse.so.0"
30808 };
30809 size_t i;
30810
30811 for (i = 0; i < ma_countof(libpulseNames); ++i) {
30812 pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
30813 if (pContext->pulse.pulseSO != NULL) {
30814 break;
30815 }
30816 }
30817
30818 if (pContext->pulse.pulseSO == NULL) {
30819 return MA_NO_BACKEND;
30820 }
30821
30822 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
30823 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
30824 pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit");
30825 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
30826 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
30827 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
30828 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
30829 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
30830 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
30831 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
30832 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
30833 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
30834 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
30835 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
30836 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
30837 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
30838 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
30839 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
30840 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
30841 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
30842 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
30843 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
30844 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
30845 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
30846 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
30847 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
30848 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
30849 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
30850 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
30851 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
30852 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
30853 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
30854 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
30855 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
30856 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
30857 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
30858 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
30859 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
30860 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
30861 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
30862 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
30863 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
30864 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
30865 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
30866 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
30867 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
30868 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
30869 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
30870 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback");
30871 pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended");
30872 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
30873 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
30874 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
30875 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
30876 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
30877 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
30878 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
30879 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
30880 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
30881 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
30882 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
30883 #else
30884 /* This strange assignment system is just for type safety. */
30885 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
30886 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
30887 ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
30888 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
30889 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
30890 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
30891 ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
30892 ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
30893 ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
30894 ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
30895 ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
30896 ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
30897 ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
30898 ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
30899 ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
30900 ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
30901 ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
30902 ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
30903 ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
30904 ma_pa_context_new_proc _pa_context_new = pa_context_new;
30905 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
30906 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
30907 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
30908 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
30909 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
30910 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
30911 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
30912 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
30913 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
30914 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
30915 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
30916 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
30917 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
30918 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
30919 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
30920 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
30921 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
30922 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
30923 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
30924 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
30925 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
30926 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
30927 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
30928 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
30929 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
30930 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
30931 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
30932 ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
30933 ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback;
30934 ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
30935 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
30936 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
30937 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
30938 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
30939 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
30940 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
30941 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
30942 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
30943 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
30944 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
30945 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
30946
30947 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
30948 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
30949 pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
30950 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
30951 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
30952 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
30953 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
30954 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
30955 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
30956 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
30957 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
30958 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
30959 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
30960 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
30961 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
30962 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
30963 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
30964 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
30965 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
30966 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
30967 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
30968 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
30969 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
30970 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
30971 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
30972 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
30973 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
30974 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
30975 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
30976 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
30977 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
30978 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
30979 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
30980 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
30981 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
30982 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
30983 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
30984 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
30985 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
30986 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
30987 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
30988 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
30989 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
30990 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
30991 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
30992 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
30993 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
30994 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
30995 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback;
30996 pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
30997 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
30998 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
30999 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
31000 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
31001 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
31002 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
31003 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
31004 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
31005 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
31006 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
31007 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
31008 #endif
31009
31010 /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */
31011 pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
31012 if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
31013 return MA_OUT_OF_MEMORY;
31014 }
31015
31016 pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
31017 if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
31018 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
31019 return MA_OUT_OF_MEMORY;
31020 }
31021
31022 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);
31023 if (result != MA_SUCCESS) {
31024 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
31025 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
31026 #ifndef MA_NO_RUNTIME_LINKING
31027 ma_dlclose(pContext, pContext->pulse.pulseSO);
31028 #endif
31029 return result;
31030 }
31031
31032 /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
31033 pCallbacks->onContextInit = ma_context_init__pulse;
31034 pCallbacks->onContextUninit = ma_context_uninit__pulse;
31035 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;
31036 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse;
31037 pCallbacks->onDeviceInit = ma_device_init__pulse;
31038 pCallbacks->onDeviceUninit = ma_device_uninit__pulse;
31039 pCallbacks->onDeviceStart = ma_device_start__pulse;
31040 pCallbacks->onDeviceStop = ma_device_stop__pulse;
31041 pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */
31042 pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */
31043 pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse;
31044 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse;
31045
31046 return MA_SUCCESS;
31047 }
31048 #endif
31049
31050
31051 /******************************************************************************
31052
31053 JACK Backend
31054
31055 ******************************************************************************/
31056 #ifdef MA_HAS_JACK
31057
31058 /* It is assumed jack.h is available when compile-time linking is being used. */
31059 #ifdef MA_NO_RUNTIME_LINKING
31060 #include <jack/jack.h>
31061
31062 typedef jack_nframes_t ma_jack_nframes_t;
31063 typedef jack_options_t ma_jack_options_t;
31064 typedef jack_status_t ma_jack_status_t;
31065 typedef jack_client_t ma_jack_client_t;
31066 typedef jack_port_t ma_jack_port_t;
31067 typedef JackProcessCallback ma_JackProcessCallback;
31068 typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
31069 typedef JackShutdownCallback ma_JackShutdownCallback;
31070 #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
31071 #define ma_JackNoStartServer JackNoStartServer
31072 #define ma_JackPortIsInput JackPortIsInput
31073 #define ma_JackPortIsOutput JackPortIsOutput
31074 #define ma_JackPortIsPhysical JackPortIsPhysical
31075 #else
31076 typedef ma_uint32 ma_jack_nframes_t;
31077 typedef int ma_jack_options_t;
31078 typedef int ma_jack_status_t;
31079 typedef struct ma_jack_client_t ma_jack_client_t;
31080 typedef struct ma_jack_port_t ma_jack_port_t;
31081 typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
31082 typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
31083 typedef void (* ma_JackShutdownCallback) (void* arg);
31084 #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
31085 #define ma_JackNoStartServer 1
31086 #define ma_JackPortIsInput 1
31087 #define ma_JackPortIsOutput 2
31088 #define ma_JackPortIsPhysical 4
31089 #endif
31090
31091 typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
31092 typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
31093 typedef int (* ma_jack_client_name_size_proc) (void);
31094 typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
31095 typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
31096 typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
31097 typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
31098 typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
31099 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);
31100 typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
31101 typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
31102 typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
31103 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);
31104 typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
31105 typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
31106 typedef void (* ma_jack_free_proc) (void* ptr);
31107
31108 static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
31109 {
31110 size_t maxClientNameSize;
31111 char clientName[256];
31112 ma_jack_status_t status;
31113 ma_jack_client_t* pClient;
31114
31115 MA_ASSERT(pContext != NULL);
31116 MA_ASSERT(ppClient != NULL);
31117
31118 if (ppClient) {
31119 *ppClient = NULL;
31120 }
31121
31122 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
31123 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
31124
31125 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
31126 if (pClient == NULL) {
31127 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31128 }
31129
31130 if (ppClient) {
31131 *ppClient = pClient;
31132 }
31133
31134 return MA_SUCCESS;
31135 }
31136
31137
31138 static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
31139 {
31140 ma_bool32 cbResult = MA_TRUE;
31141
31142 MA_ASSERT(pContext != NULL);
31143 MA_ASSERT(callback != NULL);
31144
31145 /* Playback. */
31146 if (cbResult) {
31147 ma_device_info deviceInfo;
31148 MA_ZERO_OBJECT(&deviceInfo);
31149 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31150 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
31151 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
31152 }
31153
31154 /* Capture. */
31155 if (cbResult) {
31156 ma_device_info deviceInfo;
31157 MA_ZERO_OBJECT(&deviceInfo);
31158 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31159 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
31160 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
31161 }
31162
31163 (void)cbResult; /* For silencing a static analysis warning. */
31164
31165 return MA_SUCCESS;
31166 }
31167
31168 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)
31169 {
31170 ma_jack_client_t* pClient;
31171 ma_result result;
31172 const char** ppPorts;
31173
31174 MA_ASSERT(pContext != NULL);
31175
31176 if (pDeviceID != NULL && pDeviceID->jack != 0) {
31177 return MA_NO_DEVICE; /* Don't know the device. */
31178 }
31179
31180 /* Name / Description */
31181 if (deviceType == ma_device_type_playback) {
31182 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
31183 } else {
31184 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
31185 }
31186
31187 /* Jack only uses default devices. */
31188 pDeviceInfo->isDefault = MA_TRUE;
31189
31190 /* Jack only supports f32 and has a specific channel count and sample rate. */
31191 pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
31192
31193 /* The channel count and sample rate can only be determined by opening the device. */
31194 result = ma_context_open_client__jack(pContext, &pClient);
31195 if (result != MA_SUCCESS) {
31196 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
31197 return result;
31198 }
31199
31200 pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
31201 pDeviceInfo->nativeDataFormats[0].channels = 0;
31202
31203 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));
31204 if (ppPorts == NULL) {
31205 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
31206 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
31207 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31208 }
31209
31210 while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {
31211 pDeviceInfo->nativeDataFormats[0].channels += 1;
31212 }
31213
31214 pDeviceInfo->nativeDataFormats[0].flags = 0;
31215 pDeviceInfo->nativeDataFormatCount = 1;
31216
31217 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
31218 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
31219
31220 (void)pContext;
31221 return MA_SUCCESS;
31222 }
31223
31224
31225 static ma_result ma_device_uninit__jack(ma_device* pDevice)
31226 {
31227 ma_context* pContext;
31228
31229 MA_ASSERT(pDevice != NULL);
31230
31231 pContext = pDevice->pContext;
31232 MA_ASSERT(pContext != NULL);
31233
31234 if (pDevice->jack.pClient != NULL) {
31235 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
31236 }
31237
31238 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31239 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
31240 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
31241 }
31242
31243 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31244 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
31245 ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks);
31246 }
31247
31248 return MA_SUCCESS;
31249 }
31250
31251 static void ma_device__jack_shutdown_callback(void* pUserData)
31252 {
31253 /* JACK died. Stop the device. */
31254 ma_device* pDevice = (ma_device*)pUserData;
31255 MA_ASSERT(pDevice != NULL);
31256
31257 ma_device_stop(pDevice);
31258 }
31259
31260 static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
31261 {
31262 ma_device* pDevice = (ma_device*)pUserData;
31263 MA_ASSERT(pDevice != NULL);
31264
31265 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31266 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
31267 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
31268 if (pNewBuffer == NULL) {
31269 return MA_OUT_OF_MEMORY;
31270 }
31271
31272 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
31273
31274 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
31275 pDevice->playback.internalPeriodSizeInFrames = frameCount;
31276 }
31277
31278 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31279 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
31280 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
31281 if (pNewBuffer == NULL) {
31282 return MA_OUT_OF_MEMORY;
31283 }
31284
31285 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
31286
31287 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
31288 pDevice->playback.internalPeriodSizeInFrames = frameCount;
31289 }
31290
31291 return 0;
31292 }
31293
31294 static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
31295 {
31296 ma_device* pDevice;
31297 ma_context* pContext;
31298 ma_uint32 iChannel;
31299
31300 pDevice = (ma_device*)pUserData;
31301 MA_ASSERT(pDevice != NULL);
31302
31303 pContext = pDevice->pContext;
31304 MA_ASSERT(pContext != NULL);
31305
31306 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31307 /* Channels need to be interleaved. */
31308 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
31309 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);
31310 if (pSrc != NULL) {
31311 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
31312 ma_jack_nframes_t iFrame;
31313 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31314 *pDst = *pSrc;
31315
31316 pDst += pDevice->capture.internalChannels;
31317 pSrc += 1;
31318 }
31319 }
31320 }
31321
31322 ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);
31323 }
31324
31325 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31326 ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);
31327
31328 /* Channels need to be deinterleaved. */
31329 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
31330 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount);
31331 if (pDst != NULL) {
31332 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
31333 ma_jack_nframes_t iFrame;
31334 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
31335 *pDst = *pSrc;
31336
31337 pDst += 1;
31338 pSrc += pDevice->playback.internalChannels;
31339 }
31340 }
31341 }
31342 }
31343
31344 return 0;
31345 }
31346
31347 static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
31348 {
31349 ma_result result;
31350 ma_uint32 periodSizeInFrames;
31351
31352 MA_ASSERT(pConfig != NULL);
31353 MA_ASSERT(pDevice != NULL);
31354
31355 if (pConfig->deviceType == ma_device_type_loopback) {
31356 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported.");
31357 return MA_DEVICE_TYPE_NOT_SUPPORTED;
31358 }
31359
31360 /* Only supporting default devices with JACK. */
31361 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||
31362 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) {
31363 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported.");
31364 return MA_NO_DEVICE;
31365 }
31366
31367 /* No exclusive mode with the JACK backend. */
31368 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
31369 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
31370 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported.");
31371 return MA_SHARE_MODE_NOT_SUPPORTED;
31372 }
31373
31374 /* Open the client. */
31375 result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
31376 if (result != MA_SUCCESS) {
31377 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
31378 return result;
31379 }
31380
31381 /* Callbacks. */
31382 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) {
31383 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.");
31384 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31385 }
31386 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) {
31387 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.");
31388 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31389 }
31390
31391 ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
31392
31393
31394 /* The buffer size in frames can change. */
31395 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
31396
31397 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
31398 ma_uint32 iPort;
31399 const char** ppPorts;
31400
31401 pDescriptorCapture->format = ma_format_f32;
31402 pDescriptorCapture->channels = 0;
31403 pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
31404 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
31405
31406 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);
31407 if (ppPorts == NULL) {
31408 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
31409 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31410 }
31411
31412 /* Need to count the number of ports first so we can allocate some memory. */
31413 while (ppPorts[pDescriptorCapture->channels] != NULL) {
31414 pDescriptorCapture->channels += 1;
31415 }
31416
31417 pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks);
31418 if (pDevice->jack.ppPortsCapture == NULL) {
31419 return MA_OUT_OF_MEMORY;
31420 }
31421
31422 for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) {
31423 char name[64];
31424 ma_strcpy_s(name, sizeof(name), "capture");
31425 ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
31426
31427 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);
31428 if (pDevice->jack.ppPortsCapture[iPort] == NULL) {
31429 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31430 ma_device_uninit__jack(pDevice);
31431 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
31432 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31433 }
31434 }
31435
31436 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31437
31438 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
31439 pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
31440
31441 pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);
31442 if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
31443 ma_device_uninit__jack(pDevice);
31444 return MA_OUT_OF_MEMORY;
31445 }
31446 }
31447
31448 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
31449 ma_uint32 iPort;
31450 const char** ppPorts;
31451
31452 pDescriptorPlayback->format = ma_format_f32;
31453 pDescriptorPlayback->channels = 0;
31454 pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
31455 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
31456
31457 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);
31458 if (ppPorts == NULL) {
31459 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
31460 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31461 }
31462
31463 /* Need to count the number of ports first so we can allocate some memory. */
31464 while (ppPorts[pDescriptorPlayback->channels] != NULL) {
31465 pDescriptorPlayback->channels += 1;
31466 }
31467
31468 pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks);
31469 if (pDevice->jack.ppPortsPlayback == NULL) {
31470 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
31471 return MA_OUT_OF_MEMORY;
31472 }
31473
31474 for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) {
31475 char name[64];
31476 ma_strcpy_s(name, sizeof(name), "playback");
31477 ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
31478
31479 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);
31480 if (pDevice->jack.ppPortsPlayback[iPort] == NULL) {
31481 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31482 ma_device_uninit__jack(pDevice);
31483 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
31484 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
31485 }
31486 }
31487
31488 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
31489
31490 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
31491 pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
31492
31493 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);
31494 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
31495 ma_device_uninit__jack(pDevice);
31496 return MA_OUT_OF_MEMORY;
31497 }
31498 }
31499
31500 return MA_SUCCESS;
31501 }
31502
31503
31504 static ma_result ma_device_start__jack(ma_device* pDevice)
31505 {
31506 ma_context* pContext = pDevice->pContext;
31507 int resultJACK;
31508 size_t i;
31509
31510 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
31511 if (resultJACK != 0) {
31512 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.");
31513 return MA_FAILED_TO_START_BACKEND_DEVICE;
31514 }
31515
31516 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31517 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);
31518 if (ppServerPorts == NULL) {
31519 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31520 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
31521 return MA_ERROR;
31522 }
31523
31524 for (i = 0; ppServerPorts[i] != NULL; ++i) {
31525 const char* pServerPort = ppServerPorts[i];
31526 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]);
31527
31528 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
31529 if (resultJACK != 0) {
31530 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31531 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31532 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
31533 return MA_ERROR;
31534 }
31535 }
31536
31537 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31538 }
31539
31540 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31541 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);
31542 if (ppServerPorts == NULL) {
31543 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31544 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
31545 return MA_ERROR;
31546 }
31547
31548 for (i = 0; ppServerPorts[i] != NULL; ++i) {
31549 const char* pServerPort = ppServerPorts[i];
31550 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]);
31551
31552 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
31553 if (resultJACK != 0) {
31554 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31555 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
31556 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
31557 return MA_ERROR;
31558 }
31559 }
31560
31561 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
31562 }
31563
31564 return MA_SUCCESS;
31565 }
31566
31567 static ma_result ma_device_stop__jack(ma_device* pDevice)
31568 {
31569 ma_context* pContext = pDevice->pContext;
31570
31571 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
31572 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.");
31573 return MA_ERROR;
31574 }
31575
31576 ma_device__on_notification_stopped(pDevice);
31577
31578 return MA_SUCCESS;
31579 }
31580
31581
31582 static ma_result ma_context_uninit__jack(ma_context* pContext)
31583 {
31584 MA_ASSERT(pContext != NULL);
31585 MA_ASSERT(pContext->backend == ma_backend_jack);
31586
31587 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
31588 pContext->jack.pClientName = NULL;
31589
31590 #ifndef MA_NO_RUNTIME_LINKING
31591 ma_dlclose(pContext, pContext->jack.jackSO);
31592 #endif
31593
31594 return MA_SUCCESS;
31595 }
31596
31597 static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
31598 {
31599 #ifndef MA_NO_RUNTIME_LINKING
31600 const char* libjackNames[] = {
31601 #if defined(MA_WIN32)
31602 "libjack.dll",
31603 "libjack64.dll"
31604 #endif
31605 #if defined(MA_UNIX)
31606 "libjack.so",
31607 "libjack.so.0"
31608 #endif
31609 };
31610 size_t i;
31611
31612 for (i = 0; i < ma_countof(libjackNames); ++i) {
31613 pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
31614 if (pContext->jack.jackSO != NULL) {
31615 break;
31616 }
31617 }
31618
31619 if (pContext->jack.jackSO == NULL) {
31620 return MA_NO_BACKEND;
31621 }
31622
31623 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
31624 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
31625 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
31626 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
31627 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
31628 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
31629 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
31630 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
31631 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
31632 pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
31633 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
31634 pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
31635 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
31636 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
31637 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
31638 pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
31639 #else
31640 /*
31641 This strange assignment system is here just to ensure type safety of miniaudio's function pointer
31642 types. If anything differs slightly the compiler should throw a warning.
31643 */
31644 ma_jack_client_open_proc _jack_client_open = jack_client_open;
31645 ma_jack_client_close_proc _jack_client_close = jack_client_close;
31646 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
31647 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
31648 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
31649 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
31650 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
31651 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
31652 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
31653 ma_jack_activate_proc _jack_activate = jack_activate;
31654 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
31655 ma_jack_connect_proc _jack_connect = jack_connect;
31656 ma_jack_port_register_proc _jack_port_register = jack_port_register;
31657 ma_jack_port_name_proc _jack_port_name = jack_port_name;
31658 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
31659 ma_jack_free_proc _jack_free = jack_free;
31660
31661 pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
31662 pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
31663 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
31664 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
31665 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
31666 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
31667 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
31668 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
31669 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
31670 pContext->jack.jack_activate = (ma_proc)_jack_activate;
31671 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
31672 pContext->jack.jack_connect = (ma_proc)_jack_connect;
31673 pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
31674 pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
31675 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
31676 pContext->jack.jack_free = (ma_proc)_jack_free;
31677 #endif
31678
31679 if (pConfig->jack.pClientName != NULL) {
31680 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
31681 }
31682 pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
31683
31684 /*
31685 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
31686 a temporary client.
31687 */
31688 {
31689 ma_jack_client_t* pDummyClient;
31690 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
31691 if (result != MA_SUCCESS) {
31692 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
31693 #ifndef MA_NO_RUNTIME_LINKING
31694 ma_dlclose(pContext, pContext->jack.jackSO);
31695 #endif
31696 return MA_NO_BACKEND;
31697 }
31698
31699 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
31700 }
31701
31702
31703 pCallbacks->onContextInit = ma_context_init__jack;
31704 pCallbacks->onContextUninit = ma_context_uninit__jack;
31705 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;
31706 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack;
31707 pCallbacks->onDeviceInit = ma_device_init__jack;
31708 pCallbacks->onDeviceUninit = ma_device_uninit__jack;
31709 pCallbacks->onDeviceStart = ma_device_start__jack;
31710 pCallbacks->onDeviceStop = ma_device_stop__jack;
31711 pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */
31712 pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */
31713 pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */
31714
31715 return MA_SUCCESS;
31716 }
31717 #endif /* JACK */
31718
31719
31720
31721 /******************************************************************************
31722
31723 Core Audio Backend
31724
31725 References
31726 ==========
31727 - Technical Note TN2091: Device input using the HAL Output Audio Unit
31728 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
31729
31730 ******************************************************************************/
31731 #ifdef MA_HAS_COREAUDIO
31732 #include <TargetConditionals.h>
31733
31734 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
31735 #define MA_APPLE_MOBILE
31736 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
31737 #define MA_APPLE_TV
31738 #endif
31739 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
31740 #define MA_APPLE_WATCH
31741 #endif
31742 #if __has_feature(objc_arc)
31743 #define MA_BRIDGE_TRANSFER __bridge_transfer
31744 #define MA_BRIDGE_RETAINED __bridge_retained
31745 #else
31746 #define MA_BRIDGE_TRANSFER
31747 #define MA_BRIDGE_RETAINED
31748 #endif
31749 #else
31750 #define MA_APPLE_DESKTOP
31751 #endif
31752
31753 #if defined(MA_APPLE_DESKTOP)
31754 #include <CoreAudio/CoreAudio.h>
31755 #else
31756 #include <AVFoundation/AVFoundation.h>
31757 #endif
31758
31759 #include <AudioToolbox/AudioToolbox.h>
31760
31761 /* CoreFoundation */
31762 typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
31763 typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
31764
31765 /* CoreAudio */
31766 #if defined(MA_APPLE_DESKTOP)
31767 typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
31768 typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
31769 typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
31770 typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
31771 typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
31772 #endif
31773
31774 /* AudioToolbox */
31775 typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
31776 typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
31777 typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
31778 typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
31779 typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
31780 typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
31781 typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
31782 typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
31783 typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
31784 typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
31785 typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
31786
31787
31788 #define MA_COREAUDIO_OUTPUT_BUS 0
31789 #define MA_COREAUDIO_INPUT_BUS 1
31790
31791 #if defined(MA_APPLE_DESKTOP)
31792 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
31793 #endif
31794
31795 /*
31796 Core Audio
31797
31798 So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
31799 apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
31800 needing to figure out how this darn thing works, I'm going to outline a few things here.
31801
31802 Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
31803 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
31804 that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
31805 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
31806 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
31807
31808 Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
31809 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
31810 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
31811 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
31812 the central APIs for retrieving information about the system and specific devices.
31813
31814 To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
31815 structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
31816 which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
31817 typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
31818 kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
31819 kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different.
31820
31821 Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
31822 of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
31823 address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
31824 size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
31825 AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
31826 */
31827
31828 static ma_result ma_result_from_OSStatus(OSStatus status)
31829 {
31830 switch (status)
31831 {
31832 case noErr: return MA_SUCCESS;
31833 #if defined(MA_APPLE_DESKTOP)
31834 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
31835 case kAudioHardwareUnspecifiedError: return MA_ERROR;
31836 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
31837 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
31838 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
31839 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
31840 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
31841 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
31842 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
31843 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
31844 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
31845 #endif
31846 default: return MA_ERROR;
31847 }
31848 }
31849
31850 #if 0
31851 static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
31852 {
31853 switch (bit)
31854 {
31855 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
31856 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
31857 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
31858 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
31859 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
31860 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
31861 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
31862 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
31863 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
31864 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
31865 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
31866 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
31867 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
31868 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
31869 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
31870 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
31871 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
31872 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
31873 default: return MA_CHANNEL_NONE;
31874 }
31875 }
31876 #endif
31877
31878 static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
31879 {
31880 MA_ASSERT(pDescription != NULL);
31881 MA_ASSERT(pFormatOut != NULL);
31882
31883 *pFormatOut = ma_format_unknown; /* Safety. */
31884
31885 /* There's a few things miniaudio doesn't support. */
31886 if (pDescription->mFormatID != kAudioFormatLinearPCM) {
31887 return MA_FORMAT_NOT_SUPPORTED;
31888 }
31889
31890 /* We don't support any non-packed formats that are aligned high. */
31891 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
31892 return MA_FORMAT_NOT_SUPPORTED;
31893 }
31894
31895 /* Only supporting native-endian. */
31896 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
31897 return MA_FORMAT_NOT_SUPPORTED;
31898 }
31899
31900 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
31901 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
31902 return MA_FORMAT_NOT_SUPPORTED;
31903 }*/
31904
31905 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
31906 if (pDescription->mBitsPerChannel == 32) {
31907 *pFormatOut = ma_format_f32;
31908 return MA_SUCCESS;
31909 }
31910 } else {
31911 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
31912 if (pDescription->mBitsPerChannel == 16) {
31913 *pFormatOut = ma_format_s16;
31914 return MA_SUCCESS;
31915 } else if (pDescription->mBitsPerChannel == 24) {
31916 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
31917 *pFormatOut = ma_format_s24;
31918 return MA_SUCCESS;
31919 } else {
31920 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
31921 /* TODO: Implement ma_format_s24_32. */
31922 /**pFormatOut = ma_format_s24_32;*/
31923 /*return MA_SUCCESS;*/
31924 return MA_FORMAT_NOT_SUPPORTED;
31925 }
31926 }
31927 } else if (pDescription->mBitsPerChannel == 32) {
31928 *pFormatOut = ma_format_s32;
31929 return MA_SUCCESS;
31930 }
31931 } else {
31932 if (pDescription->mBitsPerChannel == 8) {
31933 *pFormatOut = ma_format_u8;
31934 return MA_SUCCESS;
31935 }
31936 }
31937 }
31938
31939 /* Getting here means the format is not supported. */
31940 return MA_FORMAT_NOT_SUPPORTED;
31941 }
31942
31943 #if defined(MA_APPLE_DESKTOP)
31944 static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
31945 {
31946 switch (label)
31947 {
31948 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
31949 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
31950 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
31951 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
31952 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
31953 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
31954 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
31955 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
31956 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
31957 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
31958 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
31959 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
31960 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
31961 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
31962 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
31963 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
31964 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
31965 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
31966 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
31967 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
31968 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
31969 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
31970 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
31971 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
31972 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
31973 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
31974 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
31975 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
31976 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
31977 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
31978 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
31979 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
31980 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
31981 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
31982 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
31983 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
31984 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
31985 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
31986 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
31987 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
31988 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
31989 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
31990 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
31991 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
31992 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
31993 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
31994 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
31995 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
31996 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
31997 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
31998 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
31999 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
32000 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
32001 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
32002 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
32003 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
32004 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
32005 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
32006 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
32007 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
32008 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
32009 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
32010 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
32011 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
32012
32013 #if 0 /* Introduced in a later version of macOS. */
32014 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
32015 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
32016 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
32017 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
32018 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
32019 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
32020 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
32021 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
32022 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
32023 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
32024 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
32025 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
32026 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
32027 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
32028 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
32029 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
32030 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
32031 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
32032 #endif
32033
32034 default: return MA_CHANNEL_NONE;
32035 }
32036 }
32037
32038 static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
32039 {
32040 MA_ASSERT(pChannelLayout != NULL);
32041
32042 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
32043 UInt32 iChannel;
32044 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
32045 pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
32046 }
32047 } else
32048 #if 0
32049 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
32050 /* This is the same kind of system that's used by Windows audio APIs. */
32051 UInt32 iChannel = 0;
32052 UInt32 iBit;
32053 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
32054 for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
32055 AudioChannelBitmap bit = bitmap & (1 << iBit);
32056 if (bit != 0) {
32057 pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
32058 }
32059 }
32060 } else
32061 #endif
32062 {
32063 /*
32064 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
32065 be updated to determine the mapping based on the tag.
32066 */
32067 UInt32 channelCount;
32068
32069 /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */
32070 if (channelMapCap > 0xFFFFFFFF) {
32071 channelMapCap = 0xFFFFFFFF;
32072 }
32073
32074 channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
32075
32076 switch (pChannelLayout->mChannelLayoutTag)
32077 {
32078 case kAudioChannelLayoutTag_Mono:
32079 case kAudioChannelLayoutTag_Stereo:
32080 case kAudioChannelLayoutTag_StereoHeadphones:
32081 case kAudioChannelLayoutTag_MatrixStereo:
32082 case kAudioChannelLayoutTag_MidSide:
32083 case kAudioChannelLayoutTag_XY:
32084 case kAudioChannelLayoutTag_Binaural:
32085 case kAudioChannelLayoutTag_Ambisonic_B_Format:
32086 {
32087 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
32088 } break;
32089
32090 case kAudioChannelLayoutTag_Octagonal:
32091 {
32092 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
32093 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
32094 } MA_FALLTHROUGH; /* Intentional fallthrough. */
32095 case kAudioChannelLayoutTag_Hexagonal:
32096 {
32097 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
32098 } MA_FALLTHROUGH; /* Intentional fallthrough. */
32099 case kAudioChannelLayoutTag_Pentagonal:
32100 {
32101 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
32102 } MA_FALLTHROUGH; /* Intentional fallthrough. */
32103 case kAudioChannelLayoutTag_Quadraphonic:
32104 {
32105 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
32106 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
32107 pChannelMap[1] = MA_CHANNEL_RIGHT;
32108 pChannelMap[0] = MA_CHANNEL_LEFT;
32109 } break;
32110
32111 /* TODO: Add support for more tags here. */
32112
32113 default:
32114 {
32115 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
32116 } break;
32117 }
32118 }
32119
32120 return MA_SUCCESS;
32121 }
32122
32123 #if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \
32124 (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0)
32125 #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain
32126 #else
32127 /* kAudioObjectPropertyElementMaster is deprecated. */
32128 #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster
32129 #endif
32130
32131 static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
32132 {
32133 AudioObjectPropertyAddress propAddressDevices;
32134 UInt32 deviceObjectsDataSize;
32135 OSStatus status;
32136 AudioObjectID* pDeviceObjectIDs;
32137
32138 MA_ASSERT(pContext != NULL);
32139 MA_ASSERT(pDeviceCount != NULL);
32140 MA_ASSERT(ppDeviceObjectIDs != NULL);
32141
32142 /* Safety. */
32143 *pDeviceCount = 0;
32144 *ppDeviceObjectIDs = NULL;
32145
32146 propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
32147 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
32148 propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32149
32150 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
32151 if (status != noErr) {
32152 return ma_result_from_OSStatus(status);
32153 }
32154
32155 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
32156 if (pDeviceObjectIDs == NULL) {
32157 return MA_OUT_OF_MEMORY;
32158 }
32159
32160 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
32161 if (status != noErr) {
32162 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32163 return ma_result_from_OSStatus(status);
32164 }
32165
32166 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
32167 *ppDeviceObjectIDs = pDeviceObjectIDs;
32168
32169 return MA_SUCCESS;
32170 }
32171
32172 static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
32173 {
32174 AudioObjectPropertyAddress propAddress;
32175 UInt32 dataSize;
32176 OSStatus status;
32177
32178 MA_ASSERT(pContext != NULL);
32179
32180 propAddress.mSelector = kAudioDevicePropertyDeviceUID;
32181 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
32182 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32183
32184 dataSize = sizeof(*pUID);
32185 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
32186 if (status != noErr) {
32187 return ma_result_from_OSStatus(status);
32188 }
32189
32190 return MA_SUCCESS;
32191 }
32192
32193 static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
32194 {
32195 CFStringRef uid;
32196 ma_result result;
32197
32198 MA_ASSERT(pContext != NULL);
32199
32200 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
32201 if (result != MA_SUCCESS) {
32202 return result;
32203 }
32204
32205 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
32206 return MA_ERROR;
32207 }
32208
32209 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
32210 return MA_SUCCESS;
32211 }
32212
32213 static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
32214 {
32215 AudioObjectPropertyAddress propAddress;
32216 CFStringRef deviceName = NULL;
32217 UInt32 dataSize;
32218 OSStatus status;
32219
32220 MA_ASSERT(pContext != NULL);
32221
32222 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
32223 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
32224 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32225
32226 dataSize = sizeof(deviceName);
32227 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
32228 if (status != noErr) {
32229 return ma_result_from_OSStatus(status);
32230 }
32231
32232 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
32233 return MA_ERROR;
32234 }
32235
32236 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
32237 return MA_SUCCESS;
32238 }
32239
32240 static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
32241 {
32242 AudioObjectPropertyAddress propAddress;
32243 UInt32 dataSize;
32244 OSStatus status;
32245 AudioBufferList* pBufferList;
32246 ma_bool32 isSupported;
32247
32248 MA_ASSERT(pContext != NULL);
32249
32250 /* 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. */
32251 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
32252 propAddress.mScope = scope;
32253 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32254
32255 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32256 if (status != noErr) {
32257 return MA_FALSE;
32258 }
32259
32260 pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32261 if (pBufferList == NULL) {
32262 return MA_FALSE; /* Out of memory. */
32263 }
32264
32265 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
32266 if (status != noErr) {
32267 ma_free(pBufferList, &pContext->allocationCallbacks);
32268 return MA_FALSE;
32269 }
32270
32271 isSupported = MA_FALSE;
32272 if (pBufferList->mNumberBuffers > 0) {
32273 isSupported = MA_TRUE;
32274 }
32275
32276 ma_free(pBufferList, &pContext->allocationCallbacks);
32277 return isSupported;
32278 }
32279
32280 static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
32281 {
32282 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
32283 }
32284
32285 static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
32286 {
32287 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
32288 }
32289
32290
32291 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(). */
32292 {
32293 AudioObjectPropertyAddress propAddress;
32294 UInt32 dataSize;
32295 OSStatus status;
32296 AudioStreamRangedDescription* pDescriptions;
32297
32298 MA_ASSERT(pContext != NULL);
32299 MA_ASSERT(pDescriptionCount != NULL);
32300 MA_ASSERT(ppDescriptions != NULL);
32301
32302 /*
32303 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
32304 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
32305 */
32306 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
32307 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32308 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32309
32310 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32311 if (status != noErr) {
32312 return ma_result_from_OSStatus(status);
32313 }
32314
32315 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32316 if (pDescriptions == NULL) {
32317 return MA_OUT_OF_MEMORY;
32318 }
32319
32320 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
32321 if (status != noErr) {
32322 ma_free(pDescriptions, &pContext->allocationCallbacks);
32323 return ma_result_from_OSStatus(status);
32324 }
32325
32326 *pDescriptionCount = dataSize / sizeof(*pDescriptions);
32327 *ppDescriptions = pDescriptions;
32328 return MA_SUCCESS;
32329 }
32330
32331
32332 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(). */
32333 {
32334 AudioObjectPropertyAddress propAddress;
32335 UInt32 dataSize;
32336 OSStatus status;
32337 AudioChannelLayout* pChannelLayout;
32338
32339 MA_ASSERT(pContext != NULL);
32340 MA_ASSERT(ppChannelLayout != NULL);
32341
32342 *ppChannelLayout = NULL; /* Safety. */
32343
32344 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
32345 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32346 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32347
32348 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32349 if (status != noErr) {
32350 return ma_result_from_OSStatus(status);
32351 }
32352
32353 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32354 if (pChannelLayout == NULL) {
32355 return MA_OUT_OF_MEMORY;
32356 }
32357
32358 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
32359 if (status != noErr) {
32360 ma_free(pChannelLayout, &pContext->allocationCallbacks);
32361 return ma_result_from_OSStatus(status);
32362 }
32363
32364 *ppChannelLayout = pChannelLayout;
32365 return MA_SUCCESS;
32366 }
32367
32368 static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
32369 {
32370 AudioChannelLayout* pChannelLayout;
32371 ma_result result;
32372
32373 MA_ASSERT(pContext != NULL);
32374 MA_ASSERT(pChannelCount != NULL);
32375
32376 *pChannelCount = 0; /* Safety. */
32377
32378 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
32379 if (result != MA_SUCCESS) {
32380 return result;
32381 }
32382
32383 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
32384 *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
32385 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
32386 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
32387 } else {
32388 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
32389 }
32390
32391 ma_free(pChannelLayout, &pContext->allocationCallbacks);
32392 return MA_SUCCESS;
32393 }
32394
32395 #if 0
32396 static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
32397 {
32398 AudioChannelLayout* pChannelLayout;
32399 ma_result result;
32400
32401 MA_ASSERT(pContext != NULL);
32402
32403 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
32404 if (result != MA_SUCCESS) {
32405 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
32406 }
32407
32408 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
32409 if (result != MA_SUCCESS) {
32410 ma_free(pChannelLayout, &pContext->allocationCallbacks);
32411 return result;
32412 }
32413
32414 ma_free(pChannelLayout, &pContext->allocationCallbacks);
32415 return result;
32416 }
32417 #endif
32418
32419 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(). */
32420 {
32421 AudioObjectPropertyAddress propAddress;
32422 UInt32 dataSize;
32423 OSStatus status;
32424 AudioValueRange* pSampleRateRanges;
32425
32426 MA_ASSERT(pContext != NULL);
32427 MA_ASSERT(pSampleRateRangesCount != NULL);
32428 MA_ASSERT(ppSampleRateRanges != NULL);
32429
32430 /* Safety. */
32431 *pSampleRateRangesCount = 0;
32432 *ppSampleRateRanges = NULL;
32433
32434 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
32435 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32436 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32437
32438 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
32439 if (status != noErr) {
32440 return ma_result_from_OSStatus(status);
32441 }
32442
32443 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
32444 if (pSampleRateRanges == NULL) {
32445 return MA_OUT_OF_MEMORY;
32446 }
32447
32448 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
32449 if (status != noErr) {
32450 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32451 return ma_result_from_OSStatus(status);
32452 }
32453
32454 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
32455 *ppSampleRateRanges = pSampleRateRanges;
32456 return MA_SUCCESS;
32457 }
32458
32459 #if 0
32460 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)
32461 {
32462 UInt32 sampleRateRangeCount;
32463 AudioValueRange* pSampleRateRanges;
32464 ma_result result;
32465
32466 MA_ASSERT(pContext != NULL);
32467 MA_ASSERT(pSampleRateOut != NULL);
32468
32469 *pSampleRateOut = 0; /* Safety. */
32470
32471 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
32472 if (result != MA_SUCCESS) {
32473 return result;
32474 }
32475
32476 if (sampleRateRangeCount == 0) {
32477 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32478 return MA_ERROR; /* Should never hit this case should we? */
32479 }
32480
32481 if (sampleRateIn == 0) {
32482 /* Search in order of miniaudio's preferred priority. */
32483 UInt32 iMALSampleRate;
32484 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
32485 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
32486 UInt32 iCASampleRate;
32487 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
32488 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
32489 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
32490 *pSampleRateOut = malSampleRate;
32491 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32492 return MA_SUCCESS;
32493 }
32494 }
32495 }
32496
32497 /*
32498 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
32499 case we just fall back to the first one reported by Core Audio.
32500 */
32501 MA_ASSERT(sampleRateRangeCount > 0);
32502
32503 *pSampleRateOut = pSampleRateRanges[0].mMinimum;
32504 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32505 return MA_SUCCESS;
32506 } else {
32507 /* Find the closest match to this sample rate. */
32508 UInt32 currentAbsoluteDifference = INT32_MAX;
32509 UInt32 iCurrentClosestRange = (UInt32)-1;
32510 UInt32 iRange;
32511 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
32512 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
32513 *pSampleRateOut = sampleRateIn;
32514 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32515 return MA_SUCCESS;
32516 } else {
32517 UInt32 absoluteDifference;
32518 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
32519 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
32520 } else {
32521 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
32522 }
32523
32524 if (currentAbsoluteDifference > absoluteDifference) {
32525 currentAbsoluteDifference = absoluteDifference;
32526 iCurrentClosestRange = iRange;
32527 }
32528 }
32529 }
32530
32531 MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
32532
32533 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
32534 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
32535 return MA_SUCCESS;
32536 }
32537
32538 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
32539 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
32540 /*return MA_ERROR;*/
32541 }
32542 #endif
32543
32544 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)
32545 {
32546 AudioObjectPropertyAddress propAddress;
32547 AudioValueRange bufferSizeRange;
32548 UInt32 dataSize;
32549 OSStatus status;
32550
32551 MA_ASSERT(pContext != NULL);
32552 MA_ASSERT(pBufferSizeInFramesOut != NULL);
32553
32554 *pBufferSizeInFramesOut = 0; /* Safety. */
32555
32556 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
32557 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32558 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32559
32560 dataSize = sizeof(bufferSizeRange);
32561 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
32562 if (status != noErr) {
32563 return ma_result_from_OSStatus(status);
32564 }
32565
32566 /* This is just a clamp. */
32567 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
32568 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
32569 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
32570 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
32571 } else {
32572 *pBufferSizeInFramesOut = bufferSizeInFramesIn;
32573 }
32574
32575 return MA_SUCCESS;
32576 }
32577
32578 static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
32579 {
32580 ma_result result;
32581 ma_uint32 chosenBufferSizeInFrames;
32582 AudioObjectPropertyAddress propAddress;
32583 UInt32 dataSize;
32584 OSStatus status;
32585
32586 MA_ASSERT(pContext != NULL);
32587
32588 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
32589 if (result != MA_SUCCESS) {
32590 return result;
32591 }
32592
32593 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
32594 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
32595 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
32596 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32597
32598 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
32599
32600 /* Get the actual size of the buffer. */
32601 dataSize = sizeof(*pPeriodSizeInOut);
32602 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
32603 if (status != noErr) {
32604 return ma_result_from_OSStatus(status);
32605 }
32606
32607 *pPeriodSizeInOut = chosenBufferSizeInFrames;
32608 return MA_SUCCESS;
32609 }
32610
32611 static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)
32612 {
32613 AudioObjectPropertyAddress propAddressDefaultDevice;
32614 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
32615 AudioObjectID defaultDeviceObjectID;
32616 OSStatus status;
32617
32618 MA_ASSERT(pContext != NULL);
32619 MA_ASSERT(pDeviceObjectID != NULL);
32620
32621 /* Safety. */
32622 *pDeviceObjectID = 0;
32623
32624 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
32625 propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
32626 if (deviceType == ma_device_type_playback) {
32627 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
32628 } else {
32629 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
32630 }
32631
32632 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
32633 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
32634 if (status == noErr) {
32635 *pDeviceObjectID = defaultDeviceObjectID;
32636 return MA_SUCCESS;
32637 }
32638
32639 /* If we get here it means we couldn't find the device. */
32640 return MA_NO_DEVICE;
32641 }
32642
32643 static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
32644 {
32645 MA_ASSERT(pContext != NULL);
32646 MA_ASSERT(pDeviceObjectID != NULL);
32647
32648 /* Safety. */
32649 *pDeviceObjectID = 0;
32650
32651 if (pDeviceID == NULL) {
32652 /* Default device. */
32653 return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);
32654 } else {
32655 /* Explicit device. */
32656 UInt32 deviceCount;
32657 AudioObjectID* pDeviceObjectIDs;
32658 ma_result result;
32659 UInt32 iDevice;
32660
32661 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
32662 if (result != MA_SUCCESS) {
32663 return result;
32664 }
32665
32666 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
32667 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
32668
32669 char uid[256];
32670 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
32671 continue;
32672 }
32673
32674 if (deviceType == ma_device_type_playback) {
32675 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
32676 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
32677 *pDeviceObjectID = deviceObjectID;
32678 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32679 return MA_SUCCESS;
32680 }
32681 }
32682 } else {
32683 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
32684 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
32685 *pDeviceObjectID = deviceObjectID;
32686 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32687 return MA_SUCCESS;
32688 }
32689 }
32690 }
32691 }
32692
32693 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32694 }
32695
32696 /* If we get here it means we couldn't find the device. */
32697 return MA_NO_DEVICE;
32698 }
32699
32700
32701 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)
32702 {
32703 UInt32 deviceFormatDescriptionCount;
32704 AudioStreamRangedDescription* pDeviceFormatDescriptions;
32705 ma_result result;
32706 ma_uint32 desiredSampleRate;
32707 ma_uint32 desiredChannelCount;
32708 ma_format desiredFormat;
32709 AudioStreamBasicDescription bestDeviceFormatSoFar;
32710 ma_bool32 hasSupportedFormat;
32711 UInt32 iFormat;
32712
32713 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
32714 if (result != MA_SUCCESS) {
32715 return result;
32716 }
32717
32718 desiredSampleRate = sampleRate;
32719 if (desiredSampleRate == 0) {
32720 desiredSampleRate = pOrigFormat->mSampleRate;
32721 }
32722
32723 desiredChannelCount = channels;
32724 if (desiredChannelCount == 0) {
32725 desiredChannelCount = pOrigFormat->mChannelsPerFrame;
32726 }
32727
32728 desiredFormat = format;
32729 if (desiredFormat == ma_format_unknown) {
32730 result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);
32731 if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {
32732 desiredFormat = g_maFormatPriorities[0];
32733 }
32734 }
32735
32736 /*
32737 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
32738 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.
32739 */
32740 MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
32741
32742 hasSupportedFormat = MA_FALSE;
32743 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
32744 ma_format format;
32745 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
32746 if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
32747 hasSupportedFormat = MA_TRUE;
32748 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
32749 break;
32750 }
32751 }
32752
32753 if (!hasSupportedFormat) {
32754 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
32755 return MA_FORMAT_NOT_SUPPORTED;
32756 }
32757
32758
32759 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
32760 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
32761 ma_format thisSampleFormat;
32762 ma_result formatResult;
32763 ma_format bestSampleFormatSoFar;
32764
32765 /* If the format is not supported by miniaudio we need to skip this one entirely. */
32766 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
32767 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
32768 continue; /* The format is not supported by miniaudio. Skip. */
32769 }
32770
32771 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
32772
32773 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
32774 if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
32775 /*
32776 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
32777 so far has an equal sample rate we can just ignore this one.
32778 */
32779 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
32780 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. */
32781 } else {
32782 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
32783 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
32784 /* This format has a different sample rate _and_ a different channel count. */
32785 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
32786 continue; /* No change to the best format. */
32787 } else {
32788 /*
32789 Both this format and the best so far have different sample rates and different channel counts. Whichever has the
32790 best format is the new best.
32791 */
32792 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32793 bestDeviceFormatSoFar = thisDeviceFormat;
32794 continue;
32795 } else {
32796 continue; /* No change to the best format. */
32797 }
32798 }
32799 } else {
32800 /* This format has a different sample rate but the desired channel count. */
32801 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
32802 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
32803 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32804 bestDeviceFormatSoFar = thisDeviceFormat;
32805 continue;
32806 } else {
32807 continue; /* No change to the best format for now. */
32808 }
32809 } else {
32810 /* This format has the desired channel count, but the best so far does not. We have a new best. */
32811 bestDeviceFormatSoFar = thisDeviceFormat;
32812 continue;
32813 }
32814 }
32815 }
32816 } else {
32817 /*
32818 The sample rates match which makes this format a very high priority contender. If the best format so far has a different
32819 sample rate it needs to be replaced with this one.
32820 */
32821 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
32822 bestDeviceFormatSoFar = thisDeviceFormat;
32823 continue;
32824 } else {
32825 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
32826 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
32827 /*
32828 In this case this format has the same channel count as what the client is requesting. If the best format so far has
32829 a different count, this one becomes the new best.
32830 */
32831 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
32832 bestDeviceFormatSoFar = thisDeviceFormat;
32833 continue;
32834 } else {
32835 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
32836 if (thisSampleFormat == desiredFormat) {
32837 bestDeviceFormatSoFar = thisDeviceFormat;
32838 break; /* Found the exact match. */
32839 } else {
32840 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
32841 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32842 bestDeviceFormatSoFar = thisDeviceFormat;
32843 continue;
32844 } else {
32845 continue; /* No change to the best format for now. */
32846 }
32847 }
32848 }
32849 } else {
32850 /*
32851 In this case the channel count is different to what the client has requested. If the best so far has the same channel
32852 count as the requested count then it remains the best.
32853 */
32854 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
32855 continue;
32856 } else {
32857 /*
32858 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
32859 the same priority, but we need to compare the format now.
32860 */
32861 if (thisSampleFormat == bestSampleFormatSoFar) {
32862 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
32863 bestDeviceFormatSoFar = thisDeviceFormat;
32864 continue;
32865 } else {
32866 continue; /* No change to the best format for now. */
32867 }
32868 }
32869 }
32870 }
32871 }
32872 }
32873 }
32874
32875 *pFormat = bestDeviceFormatSoFar;
32876
32877 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
32878 return MA_SUCCESS;
32879 }
32880
32881 static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
32882 {
32883 AudioUnitScope deviceScope;
32884 AudioUnitElement deviceBus;
32885 UInt32 channelLayoutSize;
32886 OSStatus status;
32887 AudioChannelLayout* pChannelLayout;
32888 ma_result result;
32889
32890 MA_ASSERT(pContext != NULL);
32891
32892 if (deviceType == ma_device_type_playback) {
32893 deviceScope = kAudioUnitScope_Input;
32894 deviceBus = MA_COREAUDIO_OUTPUT_BUS;
32895 } else {
32896 deviceScope = kAudioUnitScope_Output;
32897 deviceBus = MA_COREAUDIO_INPUT_BUS;
32898 }
32899
32900 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
32901 if (status != noErr) {
32902 return ma_result_from_OSStatus(status);
32903 }
32904
32905 pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks);
32906 if (pChannelLayout == NULL) {
32907 return MA_OUT_OF_MEMORY;
32908 }
32909
32910 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
32911 if (status != noErr) {
32912 ma_free(pChannelLayout, &pContext->allocationCallbacks);
32913 return ma_result_from_OSStatus(status);
32914 }
32915
32916 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
32917 if (result != MA_SUCCESS) {
32918 ma_free(pChannelLayout, &pContext->allocationCallbacks);
32919 return result;
32920 }
32921
32922 ma_free(pChannelLayout, &pContext->allocationCallbacks);
32923 return MA_SUCCESS;
32924 }
32925 #endif /* MA_APPLE_DESKTOP */
32926
32927
32928 #if !defined(MA_APPLE_DESKTOP)
32929 static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
32930 {
32931 MA_ZERO_OBJECT(pInfo);
32932 ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
32933 ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
32934 }
32935 #endif
32936
32937 static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
32938 {
32939 #if defined(MA_APPLE_DESKTOP)
32940 UInt32 deviceCount;
32941 AudioObjectID* pDeviceObjectIDs;
32942 AudioObjectID defaultDeviceObjectIDPlayback;
32943 AudioObjectID defaultDeviceObjectIDCapture;
32944 ma_result result;
32945 UInt32 iDevice;
32946
32947 ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
32948 ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
32949
32950 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
32951 if (result != MA_SUCCESS) {
32952 return result;
32953 }
32954
32955 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
32956 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
32957 ma_device_info info;
32958
32959 MA_ZERO_OBJECT(&info);
32960 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
32961 continue;
32962 }
32963 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
32964 continue;
32965 }
32966
32967 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
32968 if (deviceObjectID == defaultDeviceObjectIDPlayback) {
32969 info.isDefault = MA_TRUE;
32970 }
32971
32972 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
32973 break;
32974 }
32975 }
32976 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
32977 if (deviceObjectID == defaultDeviceObjectIDCapture) {
32978 info.isDefault = MA_TRUE;
32979 }
32980
32981 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
32982 break;
32983 }
32984 }
32985 }
32986
32987 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
32988 #else
32989 ma_device_info info;
32990 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
32991 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
32992
32993 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
32994 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
32995 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
32996 return MA_SUCCESS;
32997 }
32998 }
32999
33000 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
33001 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
33002 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
33003 return MA_SUCCESS;
33004 }
33005 }
33006 #endif
33007
33008 return MA_SUCCESS;
33009 }
33010
33011 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)
33012 {
33013 ma_result result;
33014
33015 MA_ASSERT(pContext != NULL);
33016
33017 #if defined(MA_APPLE_DESKTOP)
33018 /* Desktop */
33019 {
33020 AudioObjectID deviceObjectID;
33021 AudioObjectID defaultDeviceObjectID;
33022 UInt32 streamDescriptionCount;
33023 AudioStreamRangedDescription* pStreamDescriptions;
33024 UInt32 iStreamDescription;
33025 UInt32 sampleRateRangeCount;
33026 AudioValueRange* pSampleRateRanges;
33027
33028 ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */
33029
33030 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
33031 if (result != MA_SUCCESS) {
33032 return result;
33033 }
33034
33035 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
33036 if (result != MA_SUCCESS) {
33037 return result;
33038 }
33039
33040 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
33041 if (result != MA_SUCCESS) {
33042 return result;
33043 }
33044
33045 if (deviceObjectID == defaultDeviceObjectID) {
33046 pDeviceInfo->isDefault = MA_TRUE;
33047 }
33048
33049 /*
33050 There could be a large number of permutations here. Fortunately there is only a single channel count
33051 being reported which reduces this quite a bit. For sample rates we're only reporting those that are
33052 one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into
33053 our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen
33054 if some driver performs software data conversion and therefore reports every possible format and
33055 sample rate.
33056 */
33057 pDeviceInfo->nativeDataFormatCount = 0;
33058
33059 /* Formats. */
33060 {
33061 ma_format uniqueFormats[ma_format_count];
33062 ma_uint32 uniqueFormatCount = 0;
33063 ma_uint32 channels;
33064
33065 /* Channels. */
33066 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
33067 if (result != MA_SUCCESS) {
33068 return result;
33069 }
33070
33071 /* Formats. */
33072 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
33073 if (result != MA_SUCCESS) {
33074 return result;
33075 }
33076
33077 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
33078 ma_format format;
33079 ma_bool32 hasFormatBeenHandled = MA_FALSE;
33080 ma_uint32 iOutputFormat;
33081 ma_uint32 iSampleRate;
33082
33083 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
33084 if (result != MA_SUCCESS) {
33085 continue;
33086 }
33087
33088 MA_ASSERT(format != ma_format_unknown);
33089
33090 /* Make sure the format isn't already in the output list. */
33091 for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
33092 if (uniqueFormats[iOutputFormat] == format) {
33093 hasFormatBeenHandled = MA_TRUE;
33094 break;
33095 }
33096 }
33097
33098 /* If we've already handled this format just skip it. */
33099 if (hasFormatBeenHandled) {
33100 continue;
33101 }
33102
33103 uniqueFormats[uniqueFormatCount] = format;
33104 uniqueFormatCount += 1;
33105
33106 /* Sample Rates */
33107 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
33108 if (result != MA_SUCCESS) {
33109 return result;
33110 }
33111
33112 /*
33113 Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
33114 between this range.
33115 */
33116 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
33117 ma_uint32 iStandardSampleRate;
33118 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
33119 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
33120 if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
33121 /* We have a new data format. Add it to the list. */
33122 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
33123 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
33124 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
33125 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
33126 pDeviceInfo->nativeDataFormatCount += 1;
33127
33128 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
33129 break; /* No more room for any more formats. */
33130 }
33131 }
33132 }
33133 }
33134
33135 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
33136
33137 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
33138 break; /* No more room for any more formats. */
33139 }
33140 }
33141
33142 ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
33143 }
33144 }
33145 #else
33146 /* Mobile */
33147 {
33148 AudioComponentDescription desc;
33149 AudioComponent component;
33150 AudioUnit audioUnit;
33151 OSStatus status;
33152 AudioUnitScope formatScope;
33153 AudioUnitElement formatElement;
33154 AudioStreamBasicDescription bestFormat;
33155 UInt32 propSize;
33156
33157 /* We want to ensure we use a consistent device name to device enumeration. */
33158 if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') {
33159 ma_bool32 found = MA_FALSE;
33160 if (deviceType == ma_device_type_playback) {
33161 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
33162 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
33163 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
33164 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
33165 found = MA_TRUE;
33166 break;
33167 }
33168 }
33169 } else {
33170 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
33171 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
33172 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
33173 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
33174 found = MA_TRUE;
33175 break;
33176 }
33177 }
33178 }
33179
33180 if (!found) {
33181 return MA_DOES_NOT_EXIST;
33182 }
33183 } else {
33184 if (deviceType == ma_device_type_playback) {
33185 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
33186 } else {
33187 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
33188 }
33189 }
33190
33191
33192 /*
33193 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
33194 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
33195 retrieve from the AVAudioSession shared instance.
33196 */
33197 desc.componentType = kAudioUnitType_Output;
33198 desc.componentSubType = kAudioUnitSubType_RemoteIO;
33199 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
33200 desc.componentFlags = 0;
33201 desc.componentFlagsMask = 0;
33202
33203 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
33204 if (component == NULL) {
33205 return MA_FAILED_TO_INIT_BACKEND;
33206 }
33207
33208 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
33209 if (status != noErr) {
33210 return ma_result_from_OSStatus(status);
33211 }
33212
33213 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
33214 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
33215
33216 propSize = sizeof(bestFormat);
33217 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
33218 if (status != noErr) {
33219 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
33220 return ma_result_from_OSStatus(status);
33221 }
33222
33223 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
33224 audioUnit = NULL;
33225
33226 /* Only a single format is being reported for iOS. */
33227 pDeviceInfo->nativeDataFormatCount = 1;
33228
33229 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
33230 if (result != MA_SUCCESS) {
33231 return result;
33232 }
33233
33234 pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
33235
33236 /*
33237 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
33238 this we just get the shared instance and inspect.
33239 */
33240 @autoreleasepool {
33241 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
33242 MA_ASSERT(pAudioSession != NULL);
33243
33244 pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
33245 }
33246 }
33247 #endif
33248
33249 (void)pDeviceInfo; /* Unused. */
33250 return MA_SUCCESS;
33251 }
33252
33253 static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
33254 {
33255 AudioBufferList* pBufferList;
33256 UInt32 audioBufferSizeInBytes;
33257 size_t allocationSize;
33258
33259 MA_ASSERT(sizeInFrames > 0);
33260 MA_ASSERT(format != ma_format_unknown);
33261 MA_ASSERT(channels > 0);
33262
33263 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
33264 if (layout == ma_stream_layout_interleaved) {
33265 /* Interleaved case. This is the simple case because we just have one buffer. */
33266 allocationSize += sizeof(AudioBuffer) * 1;
33267 } else {
33268 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
33269 allocationSize += sizeof(AudioBuffer) * channels;
33270 }
33271
33272 allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
33273
33274 pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks);
33275 if (pBufferList == NULL) {
33276 return NULL;
33277 }
33278
33279 audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));
33280
33281 if (layout == ma_stream_layout_interleaved) {
33282 pBufferList->mNumberBuffers = 1;
33283 pBufferList->mBuffers[0].mNumberChannels = channels;
33284 pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels;
33285 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
33286 } else {
33287 ma_uint32 iBuffer;
33288 pBufferList->mNumberBuffers = channels;
33289 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
33290 pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
33291 pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes;
33292 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);
33293 }
33294 }
33295
33296 return pBufferList;
33297 }
33298
33299 static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)
33300 {
33301 MA_ASSERT(pDevice != NULL);
33302 MA_ASSERT(format != ma_format_unknown);
33303 MA_ASSERT(channels > 0);
33304
33305 /* Only resize the buffer if necessary. */
33306 if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
33307 AudioBufferList* pNewAudioBufferList;
33308
33309 pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
33310 if (pNewAudioBufferList == NULL) {
33311 return MA_OUT_OF_MEMORY;
33312 }
33313
33314 /* At this point we'll have a new AudioBufferList and we can free the old one. */
33315 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
33316 pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
33317 pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
33318 }
33319
33320 /* Getting here means the capacity of the audio is fine. */
33321 return MA_SUCCESS;
33322 }
33323
33324
33325 static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
33326 {
33327 ma_device* pDevice = (ma_device*)pUserData;
33328 ma_stream_layout layout;
33329
33330 MA_ASSERT(pDevice != NULL);
33331
33332 /*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);*/
33333
33334 /* 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. */
33335 layout = ma_stream_layout_interleaved;
33336 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
33337 layout = ma_stream_layout_deinterleaved;
33338 }
33339
33340 if (layout == ma_stream_layout_interleaved) {
33341 /* For now we can assume everything is interleaved. */
33342 UInt32 iBuffer;
33343 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
33344 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
33345 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
33346 if (frameCountForThisBuffer > 0) {
33347 ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
33348 }
33349
33350 /*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);*/
33351 } else {
33352 /*
33353 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
33354 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
33355 output silence here.
33356 */
33357 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
33358 /*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);*/
33359 }
33360 }
33361 } else {
33362 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
33363 MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */
33364
33365 /*
33366 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
33367 very strange has happened and we're not going to support it.
33368 */
33369 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
33370 ma_uint8 tempBuffer[4096];
33371 UInt32 iBuffer;
33372
33373 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
33374 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
33375 ma_uint32 framesRemaining = frameCountPerBuffer;
33376
33377 while (framesRemaining > 0) {
33378 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
33379 ma_uint32 iChannel;
33380 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
33381 if (framesToRead > framesRemaining) {
33382 framesToRead = framesRemaining;
33383 }
33384
33385 ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
33386
33387 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
33388 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
33389 }
33390
33391 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
33392
33393 framesRemaining -= framesToRead;
33394 }
33395 }
33396 }
33397 }
33398
33399 (void)pActionFlags;
33400 (void)pTimeStamp;
33401 (void)busNumber;
33402 (void)frameCount;
33403
33404 return noErr;
33405 }
33406
33407 static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
33408 {
33409 ma_device* pDevice = (ma_device*)pUserData;
33410 AudioBufferList* pRenderedBufferList;
33411 ma_result result;
33412 ma_stream_layout layout;
33413 ma_uint32 iBuffer;
33414 OSStatus status;
33415
33416 MA_ASSERT(pDevice != NULL);
33417
33418 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
33419 MA_ASSERT(pRenderedBufferList);
33420
33421 /* 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. */
33422 layout = ma_stream_layout_interleaved;
33423 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
33424 layout = ma_stream_layout_deinterleaved;
33425 }
33426
33427 /*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);*/
33428
33429 /*
33430 There has been a situation reported where frame count passed into this function is greater than the capacity of
33431 our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be,
33432 so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the
33433 number of frames requested by this callback.
33434 */
33435 result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
33436 if (result != MA_SUCCESS) {
33437 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n");
33438 return noErr;
33439 }
33440
33441 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
33442 MA_ASSERT(pRenderedBufferList);
33443
33444 /*
33445 When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
33446 that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer
33447 being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a
33448 problem when a future call to this callback specifies a larger number of frames.
33449
33450 To work around this we need to explicitly set the size of each buffer to their respective size in bytes.
33451 */
33452 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
33453 pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
33454 }
33455
33456 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
33457 if (status != noErr) {
33458 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status);
33459 return status;
33460 }
33461
33462 if (layout == ma_stream_layout_interleaved) {
33463 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
33464 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
33465 ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
33466 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/
33467 } else {
33468 /*
33469 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
33470 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
33471 */
33472 ma_uint8 silentBuffer[4096];
33473 ma_uint32 framesRemaining;
33474
33475 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
33476
33477 framesRemaining = frameCount;
33478 while (framesRemaining > 0) {
33479 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
33480 if (framesToSend > framesRemaining) {
33481 framesToSend = framesRemaining;
33482 }
33483
33484 ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
33485
33486 framesRemaining -= framesToSend;
33487 }
33488
33489 /*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);*/
33490 }
33491 }
33492 } else {
33493 /* 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. */
33494 MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
33495
33496 /*
33497 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
33498 very strange has happened and we're not going to support it.
33499 */
33500 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
33501 ma_uint8 tempBuffer[4096];
33502 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
33503 ma_uint32 framesRemaining = frameCount;
33504 while (framesRemaining > 0) {
33505 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
33506 ma_uint32 iChannel;
33507 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
33508 if (framesToSend > framesRemaining) {
33509 framesToSend = framesRemaining;
33510 }
33511
33512 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
33513 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
33514 }
33515
33516 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
33517 ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
33518
33519 framesRemaining -= framesToSend;
33520 }
33521 }
33522 }
33523 }
33524
33525 (void)pActionFlags;
33526 (void)pTimeStamp;
33527 (void)busNumber;
33528 (void)frameCount;
33529 (void)pUnusedBufferList;
33530
33531 return noErr;
33532 }
33533
33534 static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
33535 {
33536 ma_device* pDevice = (ma_device*)pUserData;
33537 MA_ASSERT(pDevice != NULL);
33538
33539 /* Don't do anything if it looks like we're just reinitializing due to a device switch. */
33540 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
33541 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
33542 return;
33543 }
33544
33545 /*
33546 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
33547 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
33548 can try waiting on the same lock. I'm going to try working around this by not calling any Core
33549 Audio APIs in the callback when the device has been stopped or uninitialized.
33550 */
33551 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) {
33552 ma_device__on_notification_stopped(pDevice);
33553 } else {
33554 UInt32 isRunning;
33555 UInt32 isRunningSize = sizeof(isRunning);
33556 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
33557 if (status != noErr) {
33558 goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */
33559 }
33560
33561 if (!isRunning) {
33562 /*
33563 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:
33564
33565 1) When the device is unplugged, this will be called _before_ the default device change notification.
33566 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
33567
33568 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.
33569 */
33570 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
33571 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
33572 /*
33573 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
33574 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
33575 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
33576 hasn't!).
33577 */
33578 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
33579 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
33580 goto done;
33581 }
33582
33583 /*
33584 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
33585 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
33586 likely be successful in switching to the new device.
33587
33588 TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted.
33589 */
33590 goto done;
33591 }
33592
33593 /* Getting here means we need to stop the device. */
33594 ma_device__on_notification_stopped(pDevice);
33595 }
33596 }
33597
33598 (void)propertyID; /* Unused. */
33599
33600 done:
33601 /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */
33602 ma_event_signal(&pDevice->coreaudio.stopEvent);
33603 }
33604
33605 #if defined(MA_APPLE_DESKTOP)
33606 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. */
33607 static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
33608 static ma_mutex g_DeviceTrackingMutex_CoreAudio;
33609 static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
33610 static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
33611 static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
33612
33613 static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
33614 {
33615 ma_device_type deviceType;
33616
33617 /* Not sure if I really need to check this, but it makes me feel better. */
33618 if (addressCount == 0) {
33619 return noErr;
33620 }
33621
33622 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
33623 deviceType = ma_device_type_playback;
33624 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
33625 deviceType = ma_device_type_capture;
33626 } else {
33627 return noErr; /* Should never hit this. */
33628 }
33629
33630 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
33631 {
33632 ma_uint32 iDevice;
33633 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
33634 ma_result reinitResult;
33635 ma_device* pDevice;
33636
33637 pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
33638 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
33639 if (deviceType == ma_device_type_playback) {
33640 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
33641 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
33642 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
33643 } else {
33644 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
33645 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
33646 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
33647 }
33648
33649 if (reinitResult == MA_SUCCESS) {
33650 ma_device__post_init_setup(pDevice, deviceType);
33651
33652 /* Restart the device if required. If this fails we need to stop the device entirely. */
33653 if (ma_device_get_state(pDevice) == ma_device_state_started) {
33654 OSStatus status;
33655 if (deviceType == ma_device_type_playback) {
33656 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
33657 if (status != noErr) {
33658 if (pDevice->type == ma_device_type_duplex) {
33659 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33660 }
33661 ma_device__set_state(pDevice, ma_device_state_stopped);
33662 }
33663 } else if (deviceType == ma_device_type_capture) {
33664 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33665 if (status != noErr) {
33666 if (pDevice->type == ma_device_type_duplex) {
33667 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
33668 }
33669 ma_device__set_state(pDevice, ma_device_state_stopped);
33670 }
33671 }
33672 }
33673
33674 ma_device__on_notification_rerouted(pDevice);
33675 }
33676 }
33677 }
33678 }
33679 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33680
33681 /* Unused parameters. */
33682 (void)objectID;
33683 (void)pUserData;
33684
33685 return noErr;
33686 }
33687
33688 static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
33689 {
33690 MA_ASSERT(pContext != NULL);
33691
33692 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
33693 {
33694 /* Don't do anything if we've already initializd device tracking. */
33695 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
33696 AudioObjectPropertyAddress propAddress;
33697 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
33698 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33699
33700 ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);
33701
33702 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
33703 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33704
33705 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
33706 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33707
33708 }
33709 g_DeviceTrackingInitCounter_CoreAudio += 1;
33710 }
33711 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
33712
33713 return MA_SUCCESS;
33714 }
33715
33716 static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
33717 {
33718 MA_ASSERT(pContext != NULL);
33719
33720 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
33721 {
33722 if (g_DeviceTrackingInitCounter_CoreAudio > 0)
33723 g_DeviceTrackingInitCounter_CoreAudio -= 1;
33724
33725 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
33726 AudioObjectPropertyAddress propAddress;
33727 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
33728 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33729
33730 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
33731 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33732
33733 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
33734 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
33735
33736 /* At this point there should be no tracked devices. If not there's an error somewhere. */
33737 if (g_ppTrackedDevices_CoreAudio != NULL) {
33738 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.");
33739 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
33740 return MA_INVALID_OPERATION;
33741 }
33742
33743 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
33744 }
33745 }
33746 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
33747
33748 return MA_SUCCESS;
33749 }
33750
33751 static ma_result ma_device__track__coreaudio(ma_device* pDevice)
33752 {
33753 MA_ASSERT(pDevice != NULL);
33754
33755 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
33756 {
33757 /* Allocate memory if required. */
33758 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
33759 ma_uint32 newCap;
33760 ma_device** ppNewDevices;
33761
33762 newCap = g_TrackedDeviceCap_CoreAudio * 2;
33763 if (newCap == 0) {
33764 newCap = 1;
33765 }
33766
33767 ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks);
33768 if (ppNewDevices == NULL) {
33769 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33770 return MA_OUT_OF_MEMORY;
33771 }
33772
33773 g_ppTrackedDevices_CoreAudio = ppNewDevices;
33774 g_TrackedDeviceCap_CoreAudio = newCap;
33775 }
33776
33777 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
33778 g_TrackedDeviceCount_CoreAudio += 1;
33779 }
33780 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33781
33782 return MA_SUCCESS;
33783 }
33784
33785 static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
33786 {
33787 MA_ASSERT(pDevice != NULL);
33788
33789 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
33790 {
33791 ma_uint32 iDevice;
33792 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
33793 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
33794 /* We've found the device. We now need to remove it from the list. */
33795 ma_uint32 jDevice;
33796 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
33797 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
33798 }
33799
33800 g_TrackedDeviceCount_CoreAudio -= 1;
33801
33802 /* If there's nothing else in the list we need to free memory. */
33803 if (g_TrackedDeviceCount_CoreAudio == 0) {
33804 ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
33805 g_ppTrackedDevices_CoreAudio = NULL;
33806 g_TrackedDeviceCap_CoreAudio = 0;
33807 }
33808
33809 break;
33810 }
33811 }
33812 }
33813 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
33814
33815 return MA_SUCCESS;
33816 }
33817 #endif
33818
33819 #if defined(MA_APPLE_MOBILE)
33820 @interface ma_ios_notification_handler:NSObject {
33821 ma_device* m_pDevice;
33822 }
33823 @end
33824
33825 @implementation ma_ios_notification_handler
33826 -(id)init:(ma_device*)pDevice
33827 {
33828 self = [super init];
33829 m_pDevice = pDevice;
33830
33831 /* For route changes. */
33832 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
33833
33834 /* For interruptions. */
33835 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
33836
33837 return self;
33838 }
33839
33840 -(void)dealloc
33841 {
33842 [self remove_handler];
33843
33844 #if defined(__has_feature)
33845 #if !__has_feature(objc_arc)
33846 [super dealloc];
33847 #endif
33848 #endif
33849 }
33850
33851 -(void)remove_handler
33852 {
33853 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
33854 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
33855 }
33856
33857 -(void)handle_interruption:(NSNotification*)pNotification
33858 {
33859 NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
33860 switch (type)
33861 {
33862 case AVAudioSessionInterruptionTypeBegan:
33863 {
33864 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n");
33865
33866 /*
33867 Core Audio will have stopped the internal device automatically, but we need explicitly
33868 stop it at a higher level to ensure miniaudio-specific state is updated for consistency.
33869 */
33870 ma_device_stop(m_pDevice);
33871
33872 /*
33873 Fire the notification after the device has been stopped to ensure it's in the correct
33874 state when the notification handler is invoked.
33875 */
33876 ma_device__on_notification_interruption_began(m_pDevice);
33877 } break;
33878
33879 case AVAudioSessionInterruptionTypeEnded:
33880 {
33881 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n");
33882 ma_device__on_notification_interruption_ended(m_pDevice);
33883 } break;
33884 }
33885 }
33886
33887 -(void)handle_route_change:(NSNotification*)pNotification
33888 {
33889 AVAudioSession* pSession = [AVAudioSession sharedInstance];
33890
33891 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
33892 switch (reason)
33893 {
33894 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
33895 {
33896 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
33897 } break;
33898
33899 case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
33900 {
33901 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
33902 } break;
33903
33904 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
33905 {
33906 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
33907 } break;
33908
33909 case AVAudioSessionRouteChangeReasonWakeFromSleep:
33910 {
33911 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
33912 } break;
33913
33914 case AVAudioSessionRouteChangeReasonOverride:
33915 {
33916 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
33917 } break;
33918
33919 case AVAudioSessionRouteChangeReasonCategoryChange:
33920 {
33921 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
33922 } break;
33923
33924 case AVAudioSessionRouteChangeReasonUnknown:
33925 default:
33926 {
33927 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
33928 } break;
33929 }
33930
33931 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);
33932
33933 /* Let the application know about the route change. */
33934 ma_device__on_notification_rerouted(m_pDevice);
33935 }
33936 @end
33937 #endif
33938
33939 static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
33940 {
33941 MA_ASSERT(pDevice != NULL);
33942 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized);
33943
33944 #if defined(MA_APPLE_DESKTOP)
33945 /*
33946 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
33947 just gracefully ignore it.
33948 */
33949 ma_device__untrack__coreaudio(pDevice);
33950 #endif
33951 #if defined(MA_APPLE_MOBILE)
33952 if (pDevice->coreaudio.pNotificationHandler != NULL) {
33953 ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;
33954 [pNotificationHandler remove_handler];
33955 }
33956 #endif
33957
33958 if (pDevice->coreaudio.audioUnitCapture != NULL) {
33959 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
33960 }
33961 if (pDevice->coreaudio.audioUnitPlayback != NULL) {
33962 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
33963 }
33964
33965 if (pDevice->coreaudio.pAudioBufferList) {
33966 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
33967 }
33968
33969 return MA_SUCCESS;
33970 }
33971
33972 typedef struct
33973 {
33974 ma_bool32 allowNominalSampleRateChange;
33975
33976 /* Input. */
33977 ma_format formatIn;
33978 ma_uint32 channelsIn;
33979 ma_uint32 sampleRateIn;
33980 ma_channel channelMapIn[MA_MAX_CHANNELS];
33981 ma_uint32 periodSizeInFramesIn;
33982 ma_uint32 periodSizeInMillisecondsIn;
33983 ma_uint32 periodsIn;
33984 ma_share_mode shareMode;
33985 ma_performance_profile performanceProfile;
33986 ma_bool32 registerStopEvent;
33987
33988 /* Output. */
33989 #if defined(MA_APPLE_DESKTOP)
33990 AudioObjectID deviceObjectID;
33991 #endif
33992 AudioComponent component;
33993 AudioUnit audioUnit;
33994 AudioBufferList* pAudioBufferList; /* Only used for input devices. */
33995 ma_format formatOut;
33996 ma_uint32 channelsOut;
33997 ma_uint32 sampleRateOut;
33998 ma_channel channelMapOut[MA_MAX_CHANNELS];
33999 ma_uint32 periodSizeInFramesOut;
34000 ma_uint32 periodsOut;
34001 char deviceName[256];
34002 } ma_device_init_internal_data__coreaudio;
34003
34004 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. */
34005 {
34006 ma_result result;
34007 OSStatus status;
34008 UInt32 enableIOFlag;
34009 AudioStreamBasicDescription bestFormat;
34010 UInt32 actualPeriodSizeInFrames;
34011 AURenderCallbackStruct callbackInfo;
34012 #if defined(MA_APPLE_DESKTOP)
34013 AudioObjectID deviceObjectID;
34014 #endif
34015
34016 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
34017 if (deviceType == ma_device_type_duplex) {
34018 return MA_INVALID_ARGS;
34019 }
34020
34021 MA_ASSERT(pContext != NULL);
34022 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
34023
34024 #if defined(MA_APPLE_DESKTOP)
34025 pData->deviceObjectID = 0;
34026 #endif
34027 pData->component = NULL;
34028 pData->audioUnit = NULL;
34029 pData->pAudioBufferList = NULL;
34030
34031 #if defined(MA_APPLE_DESKTOP)
34032 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
34033 if (result != MA_SUCCESS) {
34034 return result;
34035 }
34036
34037 pData->deviceObjectID = deviceObjectID;
34038 #endif
34039
34040 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
34041 pData->periodsOut = pData->periodsIn;
34042 if (pData->periodsOut == 0) {
34043 pData->periodsOut = MA_DEFAULT_PERIODS;
34044 }
34045 if (pData->periodsOut > 16) {
34046 pData->periodsOut = 16;
34047 }
34048
34049
34050 /* Audio unit. */
34051 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
34052 if (status != noErr) {
34053 return ma_result_from_OSStatus(status);
34054 }
34055
34056
34057 /* 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. */
34058 enableIOFlag = 1;
34059 if (deviceType == ma_device_type_capture) {
34060 enableIOFlag = 0;
34061 }
34062
34063 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
34064 if (status != noErr) {
34065 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34066 return ma_result_from_OSStatus(status);
34067 }
34068
34069 enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
34070 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
34071 if (status != noErr) {
34072 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34073 return ma_result_from_OSStatus(status);
34074 }
34075
34076
34077 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
34078 #if defined(MA_APPLE_DESKTOP)
34079 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
34080 if (status != noErr) {
34081 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34082 return ma_result_from_OSStatus(result);
34083 }
34084 #else
34085 /*
34086 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
34087 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.
34088 */
34089 if (pDeviceID != NULL) {
34090 if (deviceType == ma_device_type_capture) {
34091 ma_bool32 found = MA_FALSE;
34092 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
34093 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
34094 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
34095 [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
34096 found = MA_TRUE;
34097 break;
34098 }
34099 }
34100
34101 if (found == MA_FALSE) {
34102 return MA_DOES_NOT_EXIST;
34103 }
34104 }
34105 }
34106 #endif
34107
34108 /*
34109 Format. This is the hardest part of initialization because there's a few variables to take into account.
34110 1) The format must be supported by the device.
34111 2) The format must be supported miniaudio.
34112 3) There's a priority that miniaudio prefers.
34113
34114 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
34115 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
34116 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
34117
34118 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
34119 */
34120 {
34121 AudioStreamBasicDescription origFormat;
34122 UInt32 origFormatSize = sizeof(origFormat);
34123 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
34124 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
34125
34126 if (deviceType == ma_device_type_playback) {
34127 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
34128 } else {
34129 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
34130 }
34131 if (status != noErr) {
34132 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34133 return ma_result_from_OSStatus(status);
34134 }
34135
34136 #if defined(MA_APPLE_DESKTOP)
34137 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
34138 if (result != MA_SUCCESS) {
34139 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34140 return result;
34141 }
34142
34143 /*
34144 Technical Note TN2091: Device input using the HAL Output Audio Unit
34145 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
34146
34147 This documentation says the following:
34148
34149 The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
34150 variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
34151 conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
34152 another AudioConverter.
34153
34154 The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
34155 therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
34156 safe and apply the same rule to output as well.
34157
34158 I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
34159 returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
34160 this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
34161
34162 Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
34163 this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
34164 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
34165 configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
34166 rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
34167 the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
34168 changed by miniaudio.
34169 */
34170 if (pData->allowNominalSampleRateChange) {
34171 AudioValueRange sampleRateRange;
34172 AudioObjectPropertyAddress propAddress;
34173
34174 sampleRateRange.mMinimum = bestFormat.mSampleRate;
34175 sampleRateRange.mMaximum = bestFormat.mSampleRate;
34176
34177 propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
34178 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
34179 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
34180
34181 status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
34182 if (status != noErr) {
34183 bestFormat.mSampleRate = origFormat.mSampleRate;
34184 }
34185 } else {
34186 bestFormat.mSampleRate = origFormat.mSampleRate;
34187 }
34188
34189 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
34190 if (status != noErr) {
34191 /* We failed to set the format, so fall back to the current format of the audio unit. */
34192 bestFormat = origFormat;
34193 }
34194 #else
34195 bestFormat = origFormat;
34196
34197 /*
34198 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
34199 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
34200 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
34201 can tell, it looks like the sample rate is shared between playback and capture for everything.
34202 */
34203 @autoreleasepool {
34204 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
34205 MA_ASSERT(pAudioSession != NULL);
34206
34207 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
34208 bestFormat.mSampleRate = pAudioSession.sampleRate;
34209
34210 /*
34211 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
34212 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
34213 */
34214 if (deviceType == ma_device_type_playback) {
34215 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
34216 }
34217 if (deviceType == ma_device_type_capture) {
34218 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
34219 }
34220 }
34221
34222 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
34223 if (status != noErr) {
34224 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34225 return ma_result_from_OSStatus(status);
34226 }
34227 #endif
34228
34229 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
34230 if (result != MA_SUCCESS) {
34231 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34232 return result;
34233 }
34234
34235 if (pData->formatOut == ma_format_unknown) {
34236 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34237 return MA_FORMAT_NOT_SUPPORTED;
34238 }
34239
34240 pData->channelsOut = bestFormat.mChannelsPerFrame;
34241 pData->sampleRateOut = bestFormat.mSampleRate;
34242 }
34243
34244 /* Clamp the channel count for safety. */
34245 if (pData->channelsOut > MA_MAX_CHANNELS) {
34246 pData->channelsOut = MA_MAX_CHANNELS;
34247 }
34248
34249 /*
34250 Internal channel map. This is weird in my testing. If I use the AudioObject to get the
34251 channel map, the channel descriptions are set to "Unknown" for some reason. To work around
34252 this it looks like retrieving it from the AudioUnit will work. However, and this is where
34253 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
34254 I'm going to fall back to a default assumption in these cases.
34255 */
34256 #if defined(MA_APPLE_DESKTOP)
34257 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
34258 if (result != MA_SUCCESS) {
34259 #if 0
34260 /* Try falling back to the channel map from the AudioObject. */
34261 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
34262 if (result != MA_SUCCESS) {
34263 return result;
34264 }
34265 #else
34266 /* Fall back to default assumptions. */
34267 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
34268 #endif
34269 }
34270 #else
34271 /* TODO: Figure out how to get the channel map using AVAudioSession. */
34272 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
34273 #endif
34274
34275
34276 /* Buffer size. Not allowing this to be configurable on iOS. */
34277 if (pData->periodSizeInFramesIn == 0) {
34278 if (pData->periodSizeInMillisecondsIn == 0) {
34279 if (pData->performanceProfile == ma_performance_profile_low_latency) {
34280 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);
34281 } else {
34282 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);
34283 }
34284 } else {
34285 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
34286 }
34287 } else {
34288 actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
34289 }
34290
34291 #if defined(MA_APPLE_DESKTOP)
34292 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
34293 if (result != MA_SUCCESS) {
34294 return result;
34295 }
34296 #else
34297 /*
34298 On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point
34299 number. I don't trust any potential truncation errors due to converting from float to integer
34300 so I'm going to explicitly set the actual period size to the next power of 2.
34301 */
34302 @autoreleasepool {
34303 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
34304 MA_ASSERT(pAudioSession != NULL);
34305
34306 [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];
34307 actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));
34308 }
34309 #endif
34310
34311
34312 /*
34313 During testing I discovered that the buffer size can be too big. You'll get an error like this:
34314
34315 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
34316
34317 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
34318 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
34319 */
34320 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
34321 if (status != noErr) {
34322 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34323 return ma_result_from_OSStatus(status);
34324 }
34325
34326 pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;
34327
34328 /* We need a buffer list if this is an input device. We render into this in the input callback. */
34329 if (deviceType == ma_device_type_capture) {
34330 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
34331 AudioBufferList* pBufferList;
34332
34333 pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);
34334 if (pBufferList == NULL) {
34335 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34336 return MA_OUT_OF_MEMORY;
34337 }
34338
34339 pData->pAudioBufferList = pBufferList;
34340 }
34341
34342 /* Callbacks. */
34343 callbackInfo.inputProcRefCon = pDevice_DoNotReference;
34344 if (deviceType == ma_device_type_playback) {
34345 callbackInfo.inputProc = ma_on_output__coreaudio;
34346 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
34347 if (status != noErr) {
34348 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34349 return ma_result_from_OSStatus(status);
34350 }
34351 } else {
34352 callbackInfo.inputProc = ma_on_input__coreaudio;
34353 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
34354 if (status != noErr) {
34355 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34356 return ma_result_from_OSStatus(status);
34357 }
34358 }
34359
34360 /* We need to listen for stop events. */
34361 if (pData->registerStopEvent) {
34362 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
34363 if (status != noErr) {
34364 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34365 return ma_result_from_OSStatus(status);
34366 }
34367 }
34368
34369 /* Initialize the audio unit. */
34370 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
34371 if (status != noErr) {
34372 ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks);
34373 pData->pAudioBufferList = NULL;
34374 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
34375 return ma_result_from_OSStatus(status);
34376 }
34377
34378 /* Grab the name. */
34379 #if defined(MA_APPLE_DESKTOP)
34380 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
34381 #else
34382 if (deviceType == ma_device_type_playback) {
34383 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
34384 } else {
34385 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
34386 }
34387 #endif
34388
34389 return result;
34390 }
34391
34392 #if defined(MA_APPLE_DESKTOP)
34393 static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
34394 {
34395 ma_device_init_internal_data__coreaudio data;
34396 ma_result result;
34397
34398 /* This should only be called for playback or capture, not duplex. */
34399 if (deviceType == ma_device_type_duplex) {
34400 return MA_INVALID_ARGS;
34401 }
34402
34403 data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */
34404
34405 if (deviceType == ma_device_type_capture) {
34406 data.formatIn = pDevice->capture.format;
34407 data.channelsIn = pDevice->capture.channels;
34408 data.sampleRateIn = pDevice->sampleRate;
34409 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
34410 data.shareMode = pDevice->capture.shareMode;
34411 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
34412 data.registerStopEvent = MA_TRUE;
34413
34414 if (disposePreviousAudioUnit) {
34415 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34416 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34417 }
34418 if (pDevice->coreaudio.pAudioBufferList) {
34419 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
34420 }
34421 } else if (deviceType == ma_device_type_playback) {
34422 data.formatIn = pDevice->playback.format;
34423 data.channelsIn = pDevice->playback.channels;
34424 data.sampleRateIn = pDevice->sampleRate;
34425 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
34426 data.shareMode = pDevice->playback.shareMode;
34427 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
34428 data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
34429
34430 if (disposePreviousAudioUnit) {
34431 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34432 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34433 }
34434 }
34435 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
34436 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
34437 data.periodsIn = pDevice->coreaudio.originalPeriods;
34438
34439 /* Need at least 3 periods for duplex. */
34440 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
34441 data.periodsIn = 3;
34442 }
34443
34444 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
34445 if (result != MA_SUCCESS) {
34446 return result;
34447 }
34448
34449 if (deviceType == ma_device_type_capture) {
34450 #if defined(MA_APPLE_DESKTOP)
34451 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
34452 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
34453 #endif
34454 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
34455 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
34456 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
34457
34458 pDevice->capture.internalFormat = data.formatOut;
34459 pDevice->capture.internalChannels = data.channelsOut;
34460 pDevice->capture.internalSampleRate = data.sampleRateOut;
34461 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
34462 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
34463 pDevice->capture.internalPeriods = data.periodsOut;
34464 } else if (deviceType == ma_device_type_playback) {
34465 #if defined(MA_APPLE_DESKTOP)
34466 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
34467 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
34468 #endif
34469 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
34470
34471 pDevice->playback.internalFormat = data.formatOut;
34472 pDevice->playback.internalChannels = data.channelsOut;
34473 pDevice->playback.internalSampleRate = data.sampleRateOut;
34474 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
34475 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
34476 pDevice->playback.internalPeriods = data.periodsOut;
34477 }
34478
34479 return MA_SUCCESS;
34480 }
34481 #endif /* MA_APPLE_DESKTOP */
34482
34483 static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
34484 {
34485 ma_result result;
34486
34487 MA_ASSERT(pDevice != NULL);
34488 MA_ASSERT(pConfig != NULL);
34489
34490 if (pConfig->deviceType == ma_device_type_loopback) {
34491 return MA_DEVICE_TYPE_NOT_SUPPORTED;
34492 }
34493
34494 /* No exclusive mode with the Core Audio backend for now. */
34495 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) ||
34496 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
34497 return MA_SHARE_MODE_NOT_SUPPORTED;
34498 }
34499
34500 /* Capture needs to be initialized first. */
34501 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
34502 ma_device_init_internal_data__coreaudio data;
34503 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
34504 data.formatIn = pDescriptorCapture->format;
34505 data.channelsIn = pDescriptorCapture->channels;
34506 data.sampleRateIn = pDescriptorCapture->sampleRate;
34507 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
34508 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
34509 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
34510 data.periodsIn = pDescriptorCapture->periodCount;
34511 data.shareMode = pDescriptorCapture->shareMode;
34512 data.performanceProfile = pConfig->performanceProfile;
34513 data.registerStopEvent = MA_TRUE;
34514
34515 /* Need at least 3 periods for duplex. */
34516 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
34517 data.periodsIn = 3;
34518 }
34519
34520 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
34521 if (result != MA_SUCCESS) {
34522 return result;
34523 }
34524
34525 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
34526 #if defined(MA_APPLE_DESKTOP)
34527 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
34528 #endif
34529 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
34530 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
34531 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
34532 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
34533 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
34534 pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount;
34535 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
34536
34537 pDescriptorCapture->format = data.formatOut;
34538 pDescriptorCapture->channels = data.channelsOut;
34539 pDescriptorCapture->sampleRate = data.sampleRateOut;
34540 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
34541 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
34542 pDescriptorCapture->periodCount = data.periodsOut;
34543
34544 #if defined(MA_APPLE_DESKTOP)
34545 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
34546
34547 /*
34548 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
34549 switch the device in the background.
34550 */
34551 if (pConfig->capture.pDeviceID == NULL) {
34552 ma_device__track__coreaudio(pDevice);
34553 }
34554 #endif
34555 }
34556
34557 /* Playback. */
34558 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
34559 ma_device_init_internal_data__coreaudio data;
34560 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
34561 data.formatIn = pDescriptorPlayback->format;
34562 data.channelsIn = pDescriptorPlayback->channels;
34563 data.sampleRateIn = pDescriptorPlayback->sampleRate;
34564 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
34565 data.shareMode = pDescriptorPlayback->shareMode;
34566 data.performanceProfile = pConfig->performanceProfile;
34567
34568 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
34569 if (pConfig->deviceType == ma_device_type_duplex) {
34570 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
34571 data.periodsIn = pDescriptorCapture->periodCount;
34572 data.registerStopEvent = MA_FALSE;
34573 } else {
34574 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
34575 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
34576 data.periodsIn = pDescriptorPlayback->periodCount;
34577 data.registerStopEvent = MA_TRUE;
34578 }
34579
34580 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
34581 if (result != MA_SUCCESS) {
34582 if (pConfig->deviceType == ma_device_type_duplex) {
34583 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34584 if (pDevice->coreaudio.pAudioBufferList) {
34585 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
34586 }
34587 }
34588 return result;
34589 }
34590
34591 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
34592 #if defined(MA_APPLE_DESKTOP)
34593 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
34594 #endif
34595 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
34596 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
34597 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
34598 pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
34599 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
34600
34601 pDescriptorPlayback->format = data.formatOut;
34602 pDescriptorPlayback->channels = data.channelsOut;
34603 pDescriptorPlayback->sampleRate = data.sampleRateOut;
34604 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
34605 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
34606 pDescriptorPlayback->periodCount = data.periodsOut;
34607
34608 #if defined(MA_APPLE_DESKTOP)
34609 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
34610
34611 /*
34612 If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
34613 switch the device in the background.
34614 */
34615 if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
34616 ma_device__track__coreaudio(pDevice);
34617 }
34618 #endif
34619 }
34620
34621
34622
34623 /*
34624 When stopping the device, a callback is called on another thread. We need to wait for this callback
34625 before returning from ma_device_stop(). This event is used for this.
34626 */
34627 ma_event_init(&pDevice->coreaudio.stopEvent);
34628
34629 /*
34630 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
34631 differently on non-Desktop Apple platforms.
34632 */
34633 #if defined(MA_APPLE_MOBILE)
34634 pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
34635 #endif
34636
34637 return MA_SUCCESS;
34638 }
34639
34640
34641 static ma_result ma_device_start__coreaudio(ma_device* pDevice)
34642 {
34643 MA_ASSERT(pDevice != NULL);
34644
34645 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
34646 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34647 if (status != noErr) {
34648 return ma_result_from_OSStatus(status);
34649 }
34650 }
34651
34652 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
34653 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34654 if (status != noErr) {
34655 if (pDevice->type == ma_device_type_duplex) {
34656 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34657 }
34658 return ma_result_from_OSStatus(status);
34659 }
34660 }
34661
34662 return MA_SUCCESS;
34663 }
34664
34665 static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
34666 {
34667 MA_ASSERT(pDevice != NULL);
34668
34669 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
34670
34671 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
34672 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
34673 if (status != noErr) {
34674 return ma_result_from_OSStatus(status);
34675 }
34676 }
34677
34678 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
34679 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
34680 if (status != noErr) {
34681 return ma_result_from_OSStatus(status);
34682 }
34683 }
34684
34685 /* We need to wait for the callback to finish before returning. */
34686 ma_event_wait(&pDevice->coreaudio.stopEvent);
34687 return MA_SUCCESS;
34688 }
34689
34690
34691 static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
34692 {
34693 MA_ASSERT(pContext != NULL);
34694 MA_ASSERT(pContext->backend == ma_backend_coreaudio);
34695
34696 #if defined(MA_APPLE_MOBILE)
34697 if (!pContext->coreaudio.noAudioSessionDeactivate) {
34698 if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
34699 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.");
34700 return MA_FAILED_TO_INIT_BACKEND;
34701 }
34702 }
34703 #endif
34704
34705 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34706 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
34707 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
34708 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
34709 #endif
34710
34711 #if !defined(MA_APPLE_MOBILE)
34712 ma_context__uninit_device_tracking__coreaudio(pContext);
34713 #endif
34714
34715 (void)pContext;
34716 return MA_SUCCESS;
34717 }
34718
34719 #if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0)
34720 static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
34721 {
34722 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
34723 MA_ASSERT(category != ma_ios_session_category_default);
34724 MA_ASSERT(category != ma_ios_session_category_none);
34725
34726 switch (category) {
34727 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
34728 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
34729 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
34730 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
34731 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
34732 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
34733 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
34734 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
34735 default: return AVAudioSessionCategoryAmbient;
34736 }
34737 }
34738 #endif
34739
34740 static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
34741 {
34742 #if !defined(MA_APPLE_MOBILE)
34743 ma_result result;
34744 #endif
34745
34746 MA_ASSERT(pConfig != NULL);
34747 MA_ASSERT(pContext != NULL);
34748
34749 #if defined(MA_APPLE_MOBILE)
34750 @autoreleasepool {
34751 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
34752 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
34753
34754 MA_ASSERT(pAudioSession != NULL);
34755
34756 if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
34757 /*
34758 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
34759 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
34760 */
34761 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
34762 options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
34763 #endif
34764
34765 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
34766 /* Using PlayAndRecord */
34767 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
34768 /* Using Playback */
34769 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
34770 /* Using Record */
34771 } else {
34772 /* Leave as default? */
34773 }
34774 } else {
34775 if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
34776 #if defined(__IPHONE_12_0)
34777 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
34778 return MA_INVALID_OPERATION; /* Failed to set session category. */
34779 }
34780 #else
34781 /* Ignore the session category on version 11 and older, but post a warning. */
34782 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer.");
34783 #endif
34784 }
34785 }
34786
34787 if (!pConfig->coreaudio.noAudioSessionActivate) {
34788 if (![pAudioSession setActive:true error:nil]) {
34789 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session.");
34790 return MA_FAILED_TO_INIT_BACKEND;
34791 }
34792 }
34793 }
34794 #endif
34795
34796 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34797 pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
34798 if (pContext->coreaudio.hCoreFoundation == NULL) {
34799 return MA_API_NOT_FOUND;
34800 }
34801
34802 pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
34803 pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
34804
34805
34806 pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
34807 if (pContext->coreaudio.hCoreAudio == NULL) {
34808 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
34809 return MA_API_NOT_FOUND;
34810 }
34811
34812 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
34813 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
34814 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
34815 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
34816 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
34817
34818 /*
34819 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
34820 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
34821 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
34822 AudioToolbox.
34823 */
34824 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
34825 if (pContext->coreaudio.hAudioUnit == NULL) {
34826 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
34827 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
34828 return MA_API_NOT_FOUND;
34829 }
34830
34831 if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
34832 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
34833 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
34834 pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
34835 if (pContext->coreaudio.hAudioUnit == NULL) {
34836 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
34837 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
34838 return MA_API_NOT_FOUND;
34839 }
34840 }
34841
34842 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
34843 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
34844 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
34845 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
34846 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
34847 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
34848 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
34849 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
34850 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
34851 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
34852 pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
34853 #else
34854 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
34855 pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
34856
34857 #if defined(MA_APPLE_DESKTOP)
34858 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
34859 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
34860 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
34861 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
34862 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
34863 #endif
34864
34865 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
34866 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
34867 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
34868 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
34869 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
34870 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
34871 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
34872 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
34873 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
34874 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
34875 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
34876 #endif
34877
34878 /* Audio component. */
34879 {
34880 AudioComponentDescription desc;
34881 desc.componentType = kAudioUnitType_Output;
34882 #if defined(MA_APPLE_DESKTOP)
34883 desc.componentSubType = kAudioUnitSubType_HALOutput;
34884 #else
34885 desc.componentSubType = kAudioUnitSubType_RemoteIO;
34886 #endif
34887 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
34888 desc.componentFlags = 0;
34889 desc.componentFlagsMask = 0;
34890
34891 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
34892 if (pContext->coreaudio.component == NULL) {
34893 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34894 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
34895 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
34896 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
34897 #endif
34898 return MA_FAILED_TO_INIT_BACKEND;
34899 }
34900 }
34901
34902 #if !defined(MA_APPLE_MOBILE)
34903 result = ma_context__init_device_tracking__coreaudio(pContext);
34904 if (result != MA_SUCCESS) {
34905 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
34906 ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
34907 ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
34908 ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
34909 #endif
34910 return result;
34911 }
34912 #endif
34913
34914 pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
34915
34916 pCallbacks->onContextInit = ma_context_init__coreaudio;
34917 pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
34918 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
34919 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio;
34920 pCallbacks->onDeviceInit = ma_device_init__coreaudio;
34921 pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio;
34922 pCallbacks->onDeviceStart = ma_device_start__coreaudio;
34923 pCallbacks->onDeviceStop = ma_device_stop__coreaudio;
34924 pCallbacks->onDeviceRead = NULL;
34925 pCallbacks->onDeviceWrite = NULL;
34926 pCallbacks->onDeviceDataLoop = NULL;
34927
34928 return MA_SUCCESS;
34929 }
34930 #endif /* Core Audio */
34931
34932
34933
34934 /******************************************************************************
34935
34936 sndio Backend
34937
34938 ******************************************************************************/
34939 #ifdef MA_HAS_SNDIO
34940 #include <fcntl.h>
34941
34942 /*
34943 Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
34944 to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
34945 just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
34946 demand for it or if I can get it tested and debugged more thoroughly.
34947 */
34948 #if 0
34949 #if defined(__NetBSD__) || defined(__OpenBSD__)
34950 #include <sys/audioio.h>
34951 #endif
34952 #if defined(__FreeBSD__) || defined(__DragonFly__)
34953 #include <sys/soundcard.h>
34954 #endif
34955 #endif
34956
34957 #define MA_SIO_DEVANY "default"
34958 #define MA_SIO_PLAY 1
34959 #define MA_SIO_REC 2
34960 #define MA_SIO_NENC 8
34961 #define MA_SIO_NCHAN 8
34962 #define MA_SIO_NRATE 16
34963 #define MA_SIO_NCONF 4
34964
34965 struct ma_sio_hdl; /* <-- Opaque */
34966
34967 struct ma_sio_par
34968 {
34969 unsigned int bits;
34970 unsigned int bps;
34971 unsigned int sig;
34972 unsigned int le;
34973 unsigned int msb;
34974 unsigned int rchan;
34975 unsigned int pchan;
34976 unsigned int rate;
34977 unsigned int bufsz;
34978 unsigned int xrun;
34979 unsigned int round;
34980 unsigned int appbufsz;
34981 int __pad[3];
34982 unsigned int __magic;
34983 };
34984
34985 struct ma_sio_enc
34986 {
34987 unsigned int bits;
34988 unsigned int bps;
34989 unsigned int sig;
34990 unsigned int le;
34991 unsigned int msb;
34992 };
34993
34994 struct ma_sio_conf
34995 {
34996 unsigned int enc;
34997 unsigned int rchan;
34998 unsigned int pchan;
34999 unsigned int rate;
35000 };
35001
35002 struct ma_sio_cap
35003 {
35004 struct ma_sio_enc enc[MA_SIO_NENC];
35005 unsigned int rchan[MA_SIO_NCHAN];
35006 unsigned int pchan[MA_SIO_NCHAN];
35007 unsigned int rate[MA_SIO_NRATE];
35008 int __pad[7];
35009 unsigned int nconf;
35010 struct ma_sio_conf confs[MA_SIO_NCONF];
35011 };
35012
35013 typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
35014 typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
35015 typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
35016 typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
35017 typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
35018 typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
35019 typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
35020 typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
35021 typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
35022 typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
35023
35024 static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
35025 {
35026 ma_uint32 i;
35027 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
35028 if (g_maStandardSampleRatePriorities[i] == sampleRate) {
35029 return i;
35030 }
35031 }
35032
35033 return (ma_uint32)-1;
35034 }
35035
35036 static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
35037 {
35038 /* We only support native-endian right now. */
35039 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
35040 return ma_format_unknown;
35041 }
35042
35043 if (bits == 8 && bps == 1 && sig == 0) {
35044 return ma_format_u8;
35045 }
35046 if (bits == 16 && bps == 2 && sig == 1) {
35047 return ma_format_s16;
35048 }
35049 if (bits == 24 && bps == 3 && sig == 1) {
35050 return ma_format_s24;
35051 }
35052 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
35053 /*return ma_format_s24_32;*/
35054 }
35055 if (bits == 32 && bps == 4 && sig == 1) {
35056 return ma_format_s32;
35057 }
35058
35059 return ma_format_unknown;
35060 }
35061
35062 static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
35063 {
35064 ma_format bestFormat;
35065 unsigned int iConfig;
35066
35067 MA_ASSERT(caps != NULL);
35068
35069 bestFormat = ma_format_unknown;
35070 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
35071 unsigned int iEncoding;
35072 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35073 unsigned int bits;
35074 unsigned int bps;
35075 unsigned int sig;
35076 unsigned int le;
35077 unsigned int msb;
35078 ma_format format;
35079
35080 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35081 continue;
35082 }
35083
35084 bits = caps->enc[iEncoding].bits;
35085 bps = caps->enc[iEncoding].bps;
35086 sig = caps->enc[iEncoding].sig;
35087 le = caps->enc[iEncoding].le;
35088 msb = caps->enc[iEncoding].msb;
35089 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35090 if (format == ma_format_unknown) {
35091 continue; /* Format not supported. */
35092 }
35093
35094 if (bestFormat == ma_format_unknown) {
35095 bestFormat = format;
35096 } else {
35097 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
35098 bestFormat = format;
35099 }
35100 }
35101 }
35102 }
35103
35104 return bestFormat;
35105 }
35106
35107 static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
35108 {
35109 ma_uint32 maxChannels;
35110 unsigned int iConfig;
35111
35112 MA_ASSERT(caps != NULL);
35113 MA_ASSERT(requiredFormat != ma_format_unknown);
35114
35115 /* Just pick whatever configuration has the most channels. */
35116 maxChannels = 0;
35117 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
35118 /* The encoding should be of requiredFormat. */
35119 unsigned int iEncoding;
35120 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35121 unsigned int iChannel;
35122 unsigned int bits;
35123 unsigned int bps;
35124 unsigned int sig;
35125 unsigned int le;
35126 unsigned int msb;
35127 ma_format format;
35128
35129 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35130 continue;
35131 }
35132
35133 bits = caps->enc[iEncoding].bits;
35134 bps = caps->enc[iEncoding].bps;
35135 sig = caps->enc[iEncoding].sig;
35136 le = caps->enc[iEncoding].le;
35137 msb = caps->enc[iEncoding].msb;
35138 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35139 if (format != requiredFormat) {
35140 continue;
35141 }
35142
35143 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
35144 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
35145 unsigned int chan = 0;
35146 unsigned int channels;
35147
35148 if (deviceType == ma_device_type_playback) {
35149 chan = caps->confs[iConfig].pchan;
35150 } else {
35151 chan = caps->confs[iConfig].rchan;
35152 }
35153
35154 if ((chan & (1UL << iChannel)) == 0) {
35155 continue;
35156 }
35157
35158 if (deviceType == ma_device_type_playback) {
35159 channels = caps->pchan[iChannel];
35160 } else {
35161 channels = caps->rchan[iChannel];
35162 }
35163
35164 if (maxChannels < channels) {
35165 maxChannels = channels;
35166 }
35167 }
35168 }
35169 }
35170
35171 return maxChannels;
35172 }
35173
35174 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)
35175 {
35176 ma_uint32 firstSampleRate;
35177 ma_uint32 bestSampleRate;
35178 unsigned int iConfig;
35179
35180 MA_ASSERT(caps != NULL);
35181 MA_ASSERT(requiredFormat != ma_format_unknown);
35182 MA_ASSERT(requiredChannels > 0);
35183 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
35184
35185 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
35186 bestSampleRate = 0;
35187
35188 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
35189 /* The encoding should be of requiredFormat. */
35190 unsigned int iEncoding;
35191 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35192 unsigned int iChannel;
35193 unsigned int bits;
35194 unsigned int bps;
35195 unsigned int sig;
35196 unsigned int le;
35197 unsigned int msb;
35198 ma_format format;
35199
35200 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35201 continue;
35202 }
35203
35204 bits = caps->enc[iEncoding].bits;
35205 bps = caps->enc[iEncoding].bps;
35206 sig = caps->enc[iEncoding].sig;
35207 le = caps->enc[iEncoding].le;
35208 msb = caps->enc[iEncoding].msb;
35209 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35210 if (format != requiredFormat) {
35211 continue;
35212 }
35213
35214 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
35215 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
35216 unsigned int chan = 0;
35217 unsigned int channels;
35218 unsigned int iRate;
35219
35220 if (deviceType == ma_device_type_playback) {
35221 chan = caps->confs[iConfig].pchan;
35222 } else {
35223 chan = caps->confs[iConfig].rchan;
35224 }
35225
35226 if ((chan & (1UL << iChannel)) == 0) {
35227 continue;
35228 }
35229
35230 if (deviceType == ma_device_type_playback) {
35231 channels = caps->pchan[iChannel];
35232 } else {
35233 channels = caps->rchan[iChannel];
35234 }
35235
35236 if (channels != requiredChannels) {
35237 continue;
35238 }
35239
35240 /* Getting here means we have found a compatible encoding/channel pair. */
35241 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
35242 ma_uint32 rate = (ma_uint32)caps->rate[iRate];
35243 ma_uint32 ratePriority;
35244
35245 if (firstSampleRate == 0) {
35246 firstSampleRate = rate;
35247 }
35248
35249 /* Disregard this rate if it's not a standard one. */
35250 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
35251 if (ratePriority == (ma_uint32)-1) {
35252 continue;
35253 }
35254
35255 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
35256 bestSampleRate = rate;
35257 }
35258 }
35259 }
35260 }
35261 }
35262
35263 /* If a standard sample rate was not found just fall back to the first one that was iterated. */
35264 if (bestSampleRate == 0) {
35265 bestSampleRate = firstSampleRate;
35266 }
35267
35268 return bestSampleRate;
35269 }
35270
35271
35272 static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
35273 {
35274 ma_bool32 isTerminating = MA_FALSE;
35275 struct ma_sio_hdl* handle;
35276
35277 MA_ASSERT(pContext != NULL);
35278 MA_ASSERT(callback != NULL);
35279
35280 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
35281
35282 /* Playback. */
35283 if (!isTerminating) {
35284 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
35285 if (handle != NULL) {
35286 /* Supports playback. */
35287 ma_device_info deviceInfo;
35288 MA_ZERO_OBJECT(&deviceInfo);
35289 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
35290 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
35291
35292 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
35293
35294 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
35295 }
35296 }
35297
35298 /* Capture. */
35299 if (!isTerminating) {
35300 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
35301 if (handle != NULL) {
35302 /* Supports capture. */
35303 ma_device_info deviceInfo;
35304 MA_ZERO_OBJECT(&deviceInfo);
35305 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
35306 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
35307
35308 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
35309
35310 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
35311 }
35312 }
35313
35314 return MA_SUCCESS;
35315 }
35316
35317 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)
35318 {
35319 char devid[256];
35320 struct ma_sio_hdl* handle;
35321 struct ma_sio_cap caps;
35322 unsigned int iConfig;
35323
35324 MA_ASSERT(pContext != NULL);
35325
35326 /* We need to open the device before we can get information about it. */
35327 if (pDeviceID == NULL) {
35328 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
35329 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
35330 } else {
35331 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
35332 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
35333 }
35334
35335 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
35336 if (handle == NULL) {
35337 return MA_NO_DEVICE;
35338 }
35339
35340 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
35341 return MA_ERROR;
35342 }
35343
35344 pDeviceInfo->nativeDataFormatCount = 0;
35345
35346 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
35347 /*
35348 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
35349 preference to some formats over others.
35350 */
35351 unsigned int iEncoding;
35352 unsigned int iChannel;
35353 unsigned int iRate;
35354
35355 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
35356 unsigned int bits;
35357 unsigned int bps;
35358 unsigned int sig;
35359 unsigned int le;
35360 unsigned int msb;
35361 ma_format format;
35362
35363 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
35364 continue;
35365 }
35366
35367 bits = caps.enc[iEncoding].bits;
35368 bps = caps.enc[iEncoding].bps;
35369 sig = caps.enc[iEncoding].sig;
35370 le = caps.enc[iEncoding].le;
35371 msb = caps.enc[iEncoding].msb;
35372 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
35373 if (format == ma_format_unknown) {
35374 continue; /* Format not supported. */
35375 }
35376
35377
35378 /* Channels. */
35379 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
35380 unsigned int chan = 0;
35381 unsigned int channels;
35382
35383 if (deviceType == ma_device_type_playback) {
35384 chan = caps.confs[iConfig].pchan;
35385 } else {
35386 chan = caps.confs[iConfig].rchan;
35387 }
35388
35389 if ((chan & (1UL << iChannel)) == 0) {
35390 continue;
35391 }
35392
35393 if (deviceType == ma_device_type_playback) {
35394 channels = caps.pchan[iChannel];
35395 } else {
35396 channels = caps.rchan[iChannel];
35397 }
35398
35399
35400 /* Sample Rates. */
35401 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
35402 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
35403 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);
35404 }
35405 }
35406 }
35407 }
35408 }
35409
35410 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
35411 return MA_SUCCESS;
35412 }
35413
35414 static ma_result ma_device_uninit__sndio(ma_device* pDevice)
35415 {
35416 MA_ASSERT(pDevice != NULL);
35417
35418 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35419 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
35420 }
35421
35422 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35423 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
35424 }
35425
35426 return MA_SUCCESS;
35427 }
35428
35429 static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
35430 {
35431 const char* pDeviceName;
35432 ma_ptr handle;
35433 int openFlags = 0;
35434 struct ma_sio_cap caps;
35435 struct ma_sio_par par;
35436 const ma_device_id* pDeviceID;
35437 ma_format format;
35438 ma_uint32 channels;
35439 ma_uint32 sampleRate;
35440 ma_format internalFormat;
35441 ma_uint32 internalChannels;
35442 ma_uint32 internalSampleRate;
35443 ma_uint32 internalPeriodSizeInFrames;
35444 ma_uint32 internalPeriods;
35445
35446 MA_ASSERT(pConfig != NULL);
35447 MA_ASSERT(deviceType != ma_device_type_duplex);
35448 MA_ASSERT(pDevice != NULL);
35449
35450 if (deviceType == ma_device_type_capture) {
35451 openFlags = MA_SIO_REC;
35452 } else {
35453 openFlags = MA_SIO_PLAY;
35454 }
35455
35456 pDeviceID = pDescriptor->pDeviceID;
35457 format = pDescriptor->format;
35458 channels = pDescriptor->channels;
35459 sampleRate = pDescriptor->sampleRate;
35460
35461 pDeviceName = MA_SIO_DEVANY;
35462 if (pDeviceID != NULL) {
35463 pDeviceName = pDeviceID->sndio;
35464 }
35465
35466 handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
35467 if (handle == NULL) {
35468 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.");
35469 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
35470 }
35471
35472 /* We need to retrieve the device caps to determine the most appropriate format to use. */
35473 if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
35474 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
35475 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.");
35476 return MA_ERROR;
35477 }
35478
35479 /*
35480 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
35481 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
35482 to the requested channels, regardless of whether or not the default channel count is requested.
35483
35484 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
35485 value returned by ma_find_best_channels_from_sio_cap__sndio().
35486 */
35487 if (deviceType == ma_device_type_capture) {
35488 if (format == ma_format_unknown) {
35489 format = ma_find_best_format_from_sio_cap__sndio(&caps);
35490 }
35491
35492 if (channels == 0) {
35493 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
35494 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
35495 } else {
35496 channels = MA_DEFAULT_CHANNELS;
35497 }
35498 }
35499 } else {
35500 if (format == ma_format_unknown) {
35501 format = ma_find_best_format_from_sio_cap__sndio(&caps);
35502 }
35503
35504 if (channels == 0) {
35505 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
35506 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
35507 } else {
35508 channels = MA_DEFAULT_CHANNELS;
35509 }
35510 }
35511 }
35512
35513 if (sampleRate == 0) {
35514 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
35515 }
35516
35517
35518 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
35519 par.msb = 0;
35520 par.le = ma_is_little_endian();
35521
35522 switch (format) {
35523 case ma_format_u8:
35524 {
35525 par.bits = 8;
35526 par.bps = 1;
35527 par.sig = 0;
35528 } break;
35529
35530 case ma_format_s24:
35531 {
35532 par.bits = 24;
35533 par.bps = 3;
35534 par.sig = 1;
35535 } break;
35536
35537 case ma_format_s32:
35538 {
35539 par.bits = 32;
35540 par.bps = 4;
35541 par.sig = 1;
35542 } break;
35543
35544 case ma_format_s16:
35545 case ma_format_f32:
35546 case ma_format_unknown:
35547 default:
35548 {
35549 par.bits = 16;
35550 par.bps = 2;
35551 par.sig = 1;
35552 } break;
35553 }
35554
35555 if (deviceType == ma_device_type_capture) {
35556 par.rchan = channels;
35557 } else {
35558 par.pchan = channels;
35559 }
35560
35561 par.rate = sampleRate;
35562
35563 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);
35564
35565 par.round = internalPeriodSizeInFrames;
35566 par.appbufsz = par.round * pDescriptor->periodCount;
35567
35568 if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
35569 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
35570 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.");
35571 return MA_ERROR;
35572 }
35573
35574 if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
35575 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
35576 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.");
35577 return MA_ERROR;
35578 }
35579
35580 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
35581 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
35582 internalSampleRate = par.rate;
35583 internalPeriods = par.appbufsz / par.round;
35584 internalPeriodSizeInFrames = par.round;
35585
35586 if (deviceType == ma_device_type_capture) {
35587 pDevice->sndio.handleCapture = handle;
35588 } else {
35589 pDevice->sndio.handlePlayback = handle;
35590 }
35591
35592 pDescriptor->format = internalFormat;
35593 pDescriptor->channels = internalChannels;
35594 pDescriptor->sampleRate = internalSampleRate;
35595 ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
35596 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
35597 pDescriptor->periodCount = internalPeriods;
35598
35599 return MA_SUCCESS;
35600 }
35601
35602 static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
35603 {
35604 MA_ASSERT(pDevice != NULL);
35605
35606 MA_ZERO_OBJECT(&pDevice->sndio);
35607
35608 if (pConfig->deviceType == ma_device_type_loopback) {
35609 return MA_DEVICE_TYPE_NOT_SUPPORTED;
35610 }
35611
35612 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
35613 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
35614 if (result != MA_SUCCESS) {
35615 return result;
35616 }
35617 }
35618
35619 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
35620 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
35621 if (result != MA_SUCCESS) {
35622 return result;
35623 }
35624 }
35625
35626 return MA_SUCCESS;
35627 }
35628
35629 static ma_result ma_device_start__sndio(ma_device* pDevice)
35630 {
35631 MA_ASSERT(pDevice != NULL);
35632
35633 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35634 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
35635 }
35636
35637 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
35638 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
35639 }
35640
35641 return MA_SUCCESS;
35642 }
35643
35644 static ma_result ma_device_stop__sndio(ma_device* pDevice)
35645 {
35646 MA_ASSERT(pDevice != NULL);
35647
35648 /*
35649 From the documentation:
35650
35651 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
35652 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
35653 buffer is drained. In no case are samples in the play buffer discarded.
35654
35655 Therefore, sio_stop() performs all of the necessary draining for us.
35656 */
35657
35658 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
35659 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
35660 }
35661
35662 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
35663 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
35664 }
35665
35666 return MA_SUCCESS;
35667 }
35668
35669 static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
35670 {
35671 int result;
35672
35673 if (pFramesWritten != NULL) {
35674 *pFramesWritten = 0;
35675 }
35676
35677 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));
35678 if (result == 0) {
35679 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.");
35680 return MA_IO_ERROR;
35681 }
35682
35683 if (pFramesWritten != NULL) {
35684 *pFramesWritten = frameCount;
35685 }
35686
35687 return MA_SUCCESS;
35688 }
35689
35690 static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
35691 {
35692 int result;
35693
35694 if (pFramesRead != NULL) {
35695 *pFramesRead = 0;
35696 }
35697
35698 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));
35699 if (result == 0) {
35700 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.");
35701 return MA_IO_ERROR;
35702 }
35703
35704 if (pFramesRead != NULL) {
35705 *pFramesRead = frameCount;
35706 }
35707
35708 return MA_SUCCESS;
35709 }
35710
35711 static ma_result ma_context_uninit__sndio(ma_context* pContext)
35712 {
35713 MA_ASSERT(pContext != NULL);
35714 MA_ASSERT(pContext->backend == ma_backend_sndio);
35715
35716 (void)pContext;
35717 return MA_SUCCESS;
35718 }
35719
35720 static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
35721 {
35722 #ifndef MA_NO_RUNTIME_LINKING
35723 const char* libsndioNames[] = {
35724 "libsndio.so"
35725 };
35726 size_t i;
35727
35728 for (i = 0; i < ma_countof(libsndioNames); ++i) {
35729 pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
35730 if (pContext->sndio.sndioSO != NULL) {
35731 break;
35732 }
35733 }
35734
35735 if (pContext->sndio.sndioSO == NULL) {
35736 return MA_NO_BACKEND;
35737 }
35738
35739 pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
35740 pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
35741 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
35742 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
35743 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
35744 pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
35745 pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
35746 pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
35747 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
35748 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
35749 #else
35750 pContext->sndio.sio_open = sio_open;
35751 pContext->sndio.sio_close = sio_close;
35752 pContext->sndio.sio_setpar = sio_setpar;
35753 pContext->sndio.sio_getpar = sio_getpar;
35754 pContext->sndio.sio_getcap = sio_getcap;
35755 pContext->sndio.sio_write = sio_write;
35756 pContext->sndio.sio_read = sio_read;
35757 pContext->sndio.sio_start = sio_start;
35758 pContext->sndio.sio_stop = sio_stop;
35759 pContext->sndio.sio_initpar = sio_initpar;
35760 #endif
35761
35762 pCallbacks->onContextInit = ma_context_init__sndio;
35763 pCallbacks->onContextUninit = ma_context_uninit__sndio;
35764 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;
35765 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio;
35766 pCallbacks->onDeviceInit = ma_device_init__sndio;
35767 pCallbacks->onDeviceUninit = ma_device_uninit__sndio;
35768 pCallbacks->onDeviceStart = ma_device_start__sndio;
35769 pCallbacks->onDeviceStop = ma_device_stop__sndio;
35770 pCallbacks->onDeviceRead = ma_device_read__sndio;
35771 pCallbacks->onDeviceWrite = ma_device_write__sndio;
35772 pCallbacks->onDeviceDataLoop = NULL;
35773
35774 (void)pConfig;
35775 return MA_SUCCESS;
35776 }
35777 #endif /* sndio */
35778
35779
35780
35781 /******************************************************************************
35782
35783 audio(4) Backend
35784
35785 ******************************************************************************/
35786 #ifdef MA_HAS_AUDIO4
35787 #include <fcntl.h>
35788 #include <poll.h>
35789 #include <errno.h>
35790 #include <sys/stat.h>
35791 #include <sys/types.h>
35792 #include <sys/ioctl.h>
35793 #include <sys/audioio.h>
35794
35795 #if defined(__OpenBSD__)
35796 #include <sys/param.h>
35797 #if defined(OpenBSD) && OpenBSD >= 201709
35798 #define MA_AUDIO4_USE_NEW_API
35799 #endif
35800 #endif
35801
35802 static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
35803 {
35804 size_t baseLen;
35805
35806 MA_ASSERT(id != NULL);
35807 MA_ASSERT(idSize > 0);
35808 MA_ASSERT(deviceIndex >= 0);
35809
35810 baseLen = strlen(base);
35811 MA_ASSERT(idSize > baseLen);
35812
35813 ma_strcpy_s(id, idSize, base);
35814 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
35815 }
35816
35817 static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
35818 {
35819 size_t idLen;
35820 size_t baseLen;
35821 const char* deviceIndexStr;
35822
35823 MA_ASSERT(id != NULL);
35824 MA_ASSERT(base != NULL);
35825 MA_ASSERT(pIndexOut != NULL);
35826
35827 idLen = strlen(id);
35828 baseLen = strlen(base);
35829 if (idLen <= baseLen) {
35830 return MA_ERROR; /* Doesn't look like the id starts with the base. */
35831 }
35832
35833 if (strncmp(id, base, baseLen) != 0) {
35834 return MA_ERROR; /* ID does not begin with base. */
35835 }
35836
35837 deviceIndexStr = id + baseLen;
35838 if (deviceIndexStr[0] == '\0') {
35839 return MA_ERROR; /* No index specified in the ID. */
35840 }
35841
35842 if (pIndexOut) {
35843 *pIndexOut = atoi(deviceIndexStr);
35844 }
35845
35846 return MA_SUCCESS;
35847 }
35848
35849
35850 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
35851 static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
35852 {
35853 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
35854 return ma_format_u8;
35855 } else {
35856 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
35857 if (precision == 16) {
35858 return ma_format_s16;
35859 } else if (precision == 24) {
35860 return ma_format_s24;
35861 } else if (precision == 32) {
35862 return ma_format_s32;
35863 }
35864 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
35865 if (precision == 16) {
35866 return ma_format_s16;
35867 } else if (precision == 24) {
35868 return ma_format_s24;
35869 } else if (precision == 32) {
35870 return ma_format_s32;
35871 }
35872 }
35873 }
35874
35875 return ma_format_unknown; /* Encoding not supported. */
35876 }
35877
35878 static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
35879 {
35880 MA_ASSERT(pEncoding != NULL);
35881 MA_ASSERT(pPrecision != NULL);
35882
35883 switch (format)
35884 {
35885 case ma_format_u8:
35886 {
35887 *pEncoding = AUDIO_ENCODING_ULINEAR;
35888 *pPrecision = 8;
35889 } break;
35890
35891 case ma_format_s24:
35892 {
35893 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
35894 *pPrecision = 24;
35895 } break;
35896
35897 case ma_format_s32:
35898 {
35899 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
35900 *pPrecision = 32;
35901 } break;
35902
35903 case ma_format_s16:
35904 case ma_format_f32:
35905 case ma_format_unknown:
35906 default:
35907 {
35908 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
35909 *pPrecision = 16;
35910 } break;
35911 }
35912 }
35913
35914 static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
35915 {
35916 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
35917 }
35918
35919 static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)
35920 {
35921 audio_encoding_t encoding;
35922 ma_uint32 iFormat;
35923 int counter = 0;
35924
35925 /* First check to see if the preferred format is supported. */
35926 if (preferredFormat != ma_format_unknown) {
35927 counter = 0;
35928 for (;;) {
35929 MA_ZERO_OBJECT(&encoding);
35930 encoding.index = counter;
35931 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
35932 break;
35933 }
35934
35935 if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
35936 return preferredFormat; /* Found the preferred format. */
35937 }
35938
35939 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
35940 counter += 1;
35941 }
35942 }
35943
35944 /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */
35945 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
35946 ma_format format = g_maFormatPriorities[iFormat];
35947
35948 counter = 0;
35949 for (;;) {
35950 MA_ZERO_OBJECT(&encoding);
35951 encoding.index = counter;
35952 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
35953 break;
35954 }
35955
35956 if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
35957 return format; /* Found a workable format. */
35958 }
35959
35960 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
35961 counter += 1;
35962 }
35963 }
35964
35965 /* Getting here means not appropriate format was found. */
35966 return ma_format_unknown;
35967 }
35968 #else
35969 static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
35970 {
35971 if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
35972 return ma_format_u8;
35973 }
35974 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
35975 return ma_format_s16;
35976 }
35977 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
35978 return ma_format_s24;
35979 }
35980 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
35981 return ma_format_f32;
35982 }
35983
35984 /* Format not supported. */
35985 return ma_format_unknown;
35986 }
35987 #endif
35988
35989 static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)
35990 {
35991 audio_device_t fdDevice;
35992
35993 MA_ASSERT(pContext != NULL);
35994 MA_ASSERT(fd >= 0);
35995 MA_ASSERT(pDeviceInfo != NULL);
35996
35997 (void)pContext;
35998 (void)deviceType;
35999
36000 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
36001 return MA_ERROR; /* Failed to retrieve device info. */
36002 }
36003
36004 /* Name. */
36005 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);
36006
36007 #if !defined(MA_AUDIO4_USE_NEW_API)
36008 {
36009 audio_info_t fdInfo;
36010 int counter = 0;
36011 ma_uint32 channels;
36012 ma_uint32 sampleRate;
36013
36014 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
36015 return MA_ERROR;
36016 }
36017
36018 if (deviceType == ma_device_type_playback) {
36019 channels = fdInfo.play.channels;
36020 sampleRate = fdInfo.play.sample_rate;
36021 } else {
36022 channels = fdInfo.record.channels;
36023 sampleRate = fdInfo.record.sample_rate;
36024 }
36025
36026 /* Supported formats. We get this by looking at the encodings. */
36027 pDeviceInfo->nativeDataFormatCount = 0;
36028 for (;;) {
36029 audio_encoding_t encoding;
36030 ma_format format;
36031
36032 MA_ZERO_OBJECT(&encoding);
36033 encoding.index = counter;
36034 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
36035 break;
36036 }
36037
36038 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
36039 if (format != ma_format_unknown) {
36040 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
36041 }
36042
36043 counter += 1;
36044 }
36045 }
36046 #else
36047 {
36048 struct audio_swpar fdPar;
36049 ma_format format;
36050 ma_uint32 channels;
36051 ma_uint32 sampleRate;
36052
36053 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
36054 return MA_ERROR;
36055 }
36056
36057 format = ma_format_from_swpar__audio4(&fdPar);
36058 if (format == ma_format_unknown) {
36059 return MA_FORMAT_NOT_SUPPORTED;
36060 }
36061
36062 if (deviceType == ma_device_type_playback) {
36063 channels = fdPar.pchan;
36064 } else {
36065 channels = fdPar.rchan;
36066 }
36067
36068 sampleRate = fdPar.rate;
36069
36070 pDeviceInfo->nativeDataFormatCount = 0;
36071 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
36072 }
36073 #endif
36074
36075 return MA_SUCCESS;
36076 }
36077
36078 static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
36079 {
36080 const int maxDevices = 64;
36081 char devpath[256];
36082 int iDevice;
36083
36084 MA_ASSERT(pContext != NULL);
36085 MA_ASSERT(callback != NULL);
36086
36087 /*
36088 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
36089 version here since we can open it even when another process has control of the "/dev/audioN" device.
36090 */
36091 for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
36092 struct stat st;
36093 int fd;
36094 ma_bool32 isTerminating = MA_FALSE;
36095
36096 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
36097 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
36098
36099 if (stat(devpath, &st) < 0) {
36100 break;
36101 }
36102
36103 /* The device exists, but we need to check if it's usable as playback and/or capture. */
36104
36105 /* Playback. */
36106 if (!isTerminating) {
36107 fd = open(devpath, O_RDONLY, 0);
36108 if (fd >= 0) {
36109 /* Supports playback. */
36110 ma_device_info deviceInfo;
36111 MA_ZERO_OBJECT(&deviceInfo);
36112 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
36113 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
36114 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
36115 }
36116
36117 close(fd);
36118 }
36119 }
36120
36121 /* Capture. */
36122 if (!isTerminating) {
36123 fd = open(devpath, O_WRONLY, 0);
36124 if (fd >= 0) {
36125 /* Supports capture. */
36126 ma_device_info deviceInfo;
36127 MA_ZERO_OBJECT(&deviceInfo);
36128 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
36129 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
36130 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
36131 }
36132
36133 close(fd);
36134 }
36135 }
36136
36137 if (isTerminating) {
36138 break;
36139 }
36140 }
36141
36142 return MA_SUCCESS;
36143 }
36144
36145 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)
36146 {
36147 int fd = -1;
36148 int deviceIndex = -1;
36149 char ctlid[256];
36150 ma_result result;
36151
36152 MA_ASSERT(pContext != NULL);
36153
36154 /*
36155 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
36156 from the device ID which will be in "/dev/audioN" format.
36157 */
36158 if (pDeviceID == NULL) {
36159 /* Default device. */
36160 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
36161 } else {
36162 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
36163 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
36164 if (result != MA_SUCCESS) {
36165 return result;
36166 }
36167
36168 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
36169 }
36170
36171 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
36172 if (fd == -1) {
36173 return MA_NO_DEVICE;
36174 }
36175
36176 if (deviceIndex == -1) {
36177 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
36178 } else {
36179 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
36180 }
36181
36182 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
36183
36184 close(fd);
36185 return result;
36186 }
36187
36188 static ma_result ma_device_uninit__audio4(ma_device* pDevice)
36189 {
36190 MA_ASSERT(pDevice != NULL);
36191
36192 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36193 close(pDevice->audio4.fdCapture);
36194 }
36195
36196 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36197 close(pDevice->audio4.fdPlayback);
36198 }
36199
36200 return MA_SUCCESS;
36201 }
36202
36203 static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
36204 {
36205 const char* pDefaultDeviceNames[] = {
36206 "/dev/audio",
36207 "/dev/audio0"
36208 };
36209 const char* pDefaultDeviceCtlNames[] = {
36210 "/dev/audioctl",
36211 "/dev/audioctl0"
36212 };
36213 int fd;
36214 int fdFlags = 0;
36215 size_t iDefaultDevice = (size_t)-1;
36216 ma_format internalFormat;
36217 ma_uint32 internalChannels;
36218 ma_uint32 internalSampleRate;
36219 ma_uint32 internalPeriodSizeInFrames;
36220 ma_uint32 internalPeriods;
36221
36222 MA_ASSERT(pConfig != NULL);
36223 MA_ASSERT(deviceType != ma_device_type_duplex);
36224 MA_ASSERT(pDevice != NULL);
36225
36226 /* The first thing to do is open the file. */
36227 if (deviceType == ma_device_type_capture) {
36228 fdFlags = O_RDONLY;
36229 } else {
36230 fdFlags = O_WRONLY;
36231 }
36232 /*fdFlags |= O_NONBLOCK;*/
36233
36234 /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */
36235 if (pDescriptor->pDeviceID == NULL) {
36236 /* Default device. */
36237 for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) {
36238 fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0);
36239 if (fd != -1) {
36240 break;
36241 }
36242 }
36243 } else {
36244 /* Specific device. */
36245 fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);
36246
36247 for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) {
36248 if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) {
36249 break;
36250 }
36251 }
36252
36253 if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) {
36254 iDefaultDevice = (size_t)-1;
36255 }
36256 }
36257
36258 if (fd == -1) {
36259 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.");
36260 return ma_result_from_errno(errno);
36261 }
36262
36263 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
36264 {
36265 audio_info_t fdInfo;
36266 int fdInfoResult = -1;
36267
36268 /*
36269 The documentation is a little bit unclear to me as to how it handles formats. It says the
36270 following:
36271
36272 Regardless of formats supported by underlying driver, the audio driver accepts the
36273 following formats.
36274
36275 By then the next sentence says this:
36276
36277 `encoding` and `precision` are one of the values obtained by AUDIO_GETENC.
36278
36279 It sounds like a direct contradiction to me. I'm going to play this safe any only use the
36280 best sample format returned by AUDIO_GETENC. If the requested format is supported we'll
36281 use that, but otherwise we'll just use our standard format priorities to pick an
36282 appropriate one.
36283 */
36284 AUDIO_INITINFO(&fdInfo);
36285
36286 /*
36287 Get the default format from the audioctl file if we're asking for a default device. If we
36288 retrieve it from /dev/audio it'll default to mono 8000Hz.
36289 */
36290 if (iDefaultDevice != (size_t)-1) {
36291 /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */
36292 int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
36293 if (fdctl != -1) {
36294 fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
36295 close(fdctl);
36296 }
36297 }
36298
36299 if (fdInfoResult == -1) {
36300 /* We still don't have the default device info so just retrieve it from the main audio device. */
36301 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
36302 close(fd);
36303 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
36304 return ma_result_from_errno(errno);
36305 }
36306 }
36307
36308 /* We get the driver to do as much of the data conversion as possible. */
36309 if (deviceType == ma_device_type_capture) {
36310 fdInfo.mode = AUMODE_RECORD;
36311 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);
36312
36313 if (pDescriptor->channels != 0) {
36314 fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
36315 }
36316
36317 if (pDescriptor->sampleRate != 0) {
36318 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`.) */
36319 }
36320 } else {
36321 fdInfo.mode = AUMODE_PLAY;
36322 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);
36323
36324 if (pDescriptor->channels != 0) {
36325 fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
36326 }
36327
36328 if (pDescriptor->sampleRate != 0) {
36329 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`.) */
36330 }
36331 }
36332
36333 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
36334 close(fd);
36335 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.");
36336 return ma_result_from_errno(errno);
36337 }
36338
36339 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
36340 close(fd);
36341 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
36342 return ma_result_from_errno(errno);
36343 }
36344
36345 if (deviceType == ma_device_type_capture) {
36346 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
36347 internalChannels = fdInfo.record.channels;
36348 internalSampleRate = fdInfo.record.sample_rate;
36349 } else {
36350 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
36351 internalChannels = fdInfo.play.channels;
36352 internalSampleRate = fdInfo.play.sample_rate;
36353 }
36354
36355 if (internalFormat == ma_format_unknown) {
36356 close(fd);
36357 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.");
36358 return MA_FORMAT_NOT_SUPPORTED;
36359 }
36360
36361 /* Buffer. */
36362 {
36363 ma_uint32 internalPeriodSizeInBytes;
36364
36365 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
36366
36367 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
36368 if (internalPeriodSizeInBytes < 16) {
36369 internalPeriodSizeInBytes = 16;
36370 }
36371
36372 internalPeriods = pDescriptor->periodCount;
36373 if (internalPeriods < 2) {
36374 internalPeriods = 2;
36375 }
36376
36377 /* What miniaudio calls a period, audio4 calls a block. */
36378 AUDIO_INITINFO(&fdInfo);
36379 fdInfo.hiwat = internalPeriods;
36380 fdInfo.lowat = internalPeriods-1;
36381 fdInfo.blocksize = internalPeriodSizeInBytes;
36382 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
36383 close(fd);
36384 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.");
36385 return ma_result_from_errno(errno);
36386 }
36387
36388 internalPeriods = fdInfo.hiwat;
36389 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
36390 }
36391 }
36392 #else
36393 {
36394 struct audio_swpar fdPar;
36395
36396 /* 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. */
36397 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
36398 close(fd);
36399 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.");
36400 return ma_result_from_errno(errno);
36401 }
36402
36403 internalFormat = ma_format_from_swpar__audio4(&fdPar);
36404 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
36405 internalSampleRate = fdPar.rate;
36406
36407 if (internalFormat == ma_format_unknown) {
36408 close(fd);
36409 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.");
36410 return MA_FORMAT_NOT_SUPPORTED;
36411 }
36412
36413 /* Buffer. */
36414 {
36415 ma_uint32 internalPeriodSizeInBytes;
36416
36417 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
36418
36419 /* What miniaudio calls a period, audio4 calls a block. */
36420 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
36421 if (internalPeriodSizeInBytes < 16) {
36422 internalPeriodSizeInBytes = 16;
36423 }
36424
36425 fdPar.nblks = pDescriptor->periodCount;
36426 fdPar.round = internalPeriodSizeInBytes;
36427
36428 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
36429 close(fd);
36430 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.");
36431 return ma_result_from_errno(errno);
36432 }
36433
36434 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
36435 close(fd);
36436 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.");
36437 return ma_result_from_errno(errno);
36438 }
36439 }
36440
36441 internalFormat = ma_format_from_swpar__audio4(&fdPar);
36442 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
36443 internalSampleRate = fdPar.rate;
36444 internalPeriods = fdPar.nblks;
36445 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
36446 }
36447 #endif
36448
36449 if (internalFormat == ma_format_unknown) {
36450 close(fd);
36451 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.");
36452 return MA_FORMAT_NOT_SUPPORTED;
36453 }
36454
36455 if (deviceType == ma_device_type_capture) {
36456 pDevice->audio4.fdCapture = fd;
36457 } else {
36458 pDevice->audio4.fdPlayback = fd;
36459 }
36460
36461 pDescriptor->format = internalFormat;
36462 pDescriptor->channels = internalChannels;
36463 pDescriptor->sampleRate = internalSampleRate;
36464 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
36465 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
36466 pDescriptor->periodCount = internalPeriods;
36467
36468 return MA_SUCCESS;
36469 }
36470
36471 static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
36472 {
36473 MA_ASSERT(pDevice != NULL);
36474
36475 MA_ZERO_OBJECT(&pDevice->audio4);
36476
36477 if (pConfig->deviceType == ma_device_type_loopback) {
36478 return MA_DEVICE_TYPE_NOT_SUPPORTED;
36479 }
36480
36481 pDevice->audio4.fdCapture = -1;
36482 pDevice->audio4.fdPlayback = -1;
36483
36484 /*
36485 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
36486 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
36487 I'm aware.
36488 */
36489 #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
36490 /* NetBSD 8.0+ */
36491 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
36492 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
36493 return MA_SHARE_MODE_NOT_SUPPORTED;
36494 }
36495 #else
36496 /* All other flavors. */
36497 #endif
36498
36499 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
36500 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
36501 if (result != MA_SUCCESS) {
36502 return result;
36503 }
36504 }
36505
36506 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
36507 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
36508 if (result != MA_SUCCESS) {
36509 if (pConfig->deviceType == ma_device_type_duplex) {
36510 close(pDevice->audio4.fdCapture);
36511 }
36512 return result;
36513 }
36514 }
36515
36516 return MA_SUCCESS;
36517 }
36518
36519 static ma_result ma_device_start__audio4(ma_device* pDevice)
36520 {
36521 MA_ASSERT(pDevice != NULL);
36522
36523 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36524 if (pDevice->audio4.fdCapture == -1) {
36525 return MA_INVALID_ARGS;
36526 }
36527 }
36528
36529 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36530 if (pDevice->audio4.fdPlayback == -1) {
36531 return MA_INVALID_ARGS;
36532 }
36533 }
36534
36535 return MA_SUCCESS;
36536 }
36537
36538 static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
36539 {
36540 if (fd == -1) {
36541 return MA_INVALID_ARGS;
36542 }
36543
36544 #if !defined(MA_AUDIO4_USE_NEW_API)
36545 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
36546 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.");
36547 return ma_result_from_errno(errno);
36548 }
36549 #else
36550 if (ioctl(fd, AUDIO_STOP, 0) < 0) {
36551 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.");
36552 return ma_result_from_errno(errno);
36553 }
36554 #endif
36555
36556 return MA_SUCCESS;
36557 }
36558
36559 static ma_result ma_device_stop__audio4(ma_device* pDevice)
36560 {
36561 MA_ASSERT(pDevice != NULL);
36562
36563 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36564 ma_result result;
36565
36566 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
36567 if (result != MA_SUCCESS) {
36568 return result;
36569 }
36570 }
36571
36572 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36573 ma_result result;
36574
36575 /* 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. */
36576 #if !defined(MA_AUDIO4_USE_NEW_API)
36577 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
36578 #endif
36579
36580 /* Here is where the device is stopped immediately. */
36581 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
36582 if (result != MA_SUCCESS) {
36583 return result;
36584 }
36585 }
36586
36587 return MA_SUCCESS;
36588 }
36589
36590 static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
36591 {
36592 int result;
36593
36594 if (pFramesWritten != NULL) {
36595 *pFramesWritten = 0;
36596 }
36597
36598 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
36599 if (result < 0) {
36600 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.");
36601 return ma_result_from_errno(errno);
36602 }
36603
36604 if (pFramesWritten != NULL) {
36605 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
36606 }
36607
36608 return MA_SUCCESS;
36609 }
36610
36611 static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
36612 {
36613 int result;
36614
36615 if (pFramesRead != NULL) {
36616 *pFramesRead = 0;
36617 }
36618
36619 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
36620 if (result < 0) {
36621 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.");
36622 return ma_result_from_errno(errno);
36623 }
36624
36625 if (pFramesRead != NULL) {
36626 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
36627 }
36628
36629 return MA_SUCCESS;
36630 }
36631
36632 static ma_result ma_context_uninit__audio4(ma_context* pContext)
36633 {
36634 MA_ASSERT(pContext != NULL);
36635 MA_ASSERT(pContext->backend == ma_backend_audio4);
36636
36637 (void)pContext;
36638 return MA_SUCCESS;
36639 }
36640
36641 static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
36642 {
36643 MA_ASSERT(pContext != NULL);
36644
36645 (void)pConfig;
36646
36647 pCallbacks->onContextInit = ma_context_init__audio4;
36648 pCallbacks->onContextUninit = ma_context_uninit__audio4;
36649 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;
36650 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4;
36651 pCallbacks->onDeviceInit = ma_device_init__audio4;
36652 pCallbacks->onDeviceUninit = ma_device_uninit__audio4;
36653 pCallbacks->onDeviceStart = ma_device_start__audio4;
36654 pCallbacks->onDeviceStop = ma_device_stop__audio4;
36655 pCallbacks->onDeviceRead = ma_device_read__audio4;
36656 pCallbacks->onDeviceWrite = ma_device_write__audio4;
36657 pCallbacks->onDeviceDataLoop = NULL;
36658
36659 return MA_SUCCESS;
36660 }
36661 #endif /* audio4 */
36662
36663
36664 /******************************************************************************
36665
36666 OSS Backend
36667
36668 ******************************************************************************/
36669 #ifdef MA_HAS_OSS
36670 #include <sys/ioctl.h>
36671 #include <unistd.h>
36672 #include <fcntl.h>
36673 #include <sys/soundcard.h>
36674
36675 #ifndef SNDCTL_DSP_HALT
36676 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
36677 #endif
36678
36679 #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp"
36680
36681 static int ma_open_temp_device__oss()
36682 {
36683 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
36684 int fd = open("/dev/mixer", O_RDONLY, 0);
36685 if (fd >= 0) {
36686 return fd;
36687 }
36688
36689 return -1;
36690 }
36691
36692 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)
36693 {
36694 const char* deviceName;
36695 int flags;
36696
36697 MA_ASSERT(pContext != NULL);
36698 MA_ASSERT(pfd != NULL);
36699 (void)pContext;
36700
36701 *pfd = -1;
36702
36703 /* This function should only be called for playback or capture, not duplex. */
36704 if (deviceType == ma_device_type_duplex) {
36705 return MA_INVALID_ARGS;
36706 }
36707
36708 deviceName = MA_OSS_DEFAULT_DEVICE_NAME;
36709 if (pDeviceID != NULL) {
36710 deviceName = pDeviceID->oss;
36711 }
36712
36713 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
36714 if (shareMode == ma_share_mode_exclusive) {
36715 flags |= O_EXCL;
36716 }
36717
36718 *pfd = open(deviceName, flags, 0);
36719 if (*pfd == -1) {
36720 return ma_result_from_errno(errno);
36721 }
36722
36723 return MA_SUCCESS;
36724 }
36725
36726 static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
36727 {
36728 int fd;
36729 oss_sysinfo si;
36730 int result;
36731
36732 MA_ASSERT(pContext != NULL);
36733 MA_ASSERT(callback != NULL);
36734
36735 fd = ma_open_temp_device__oss();
36736 if (fd == -1) {
36737 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.");
36738 return MA_NO_BACKEND;
36739 }
36740
36741 result = ioctl(fd, SNDCTL_SYSINFO, &si);
36742 if (result != -1) {
36743 int iAudioDevice;
36744 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
36745 oss_audioinfo ai;
36746 ai.dev = iAudioDevice;
36747 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
36748 if (result != -1) {
36749 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
36750 ma_device_info deviceInfo;
36751 ma_bool32 isTerminating = MA_FALSE;
36752
36753 MA_ZERO_OBJECT(&deviceInfo);
36754
36755 /* ID */
36756 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
36757
36758 /*
36759 The human readable device name should be in the "ai.handle" variable, but it can
36760 sometimes be empty in which case we just fall back to "ai.name" which is less user
36761 friendly, but usually has a value.
36762 */
36763 if (ai.handle[0] != '\0') {
36764 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
36765 } else {
36766 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
36767 }
36768
36769 /* The device can be both playback and capture. */
36770 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
36771 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
36772 }
36773 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
36774 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
36775 }
36776
36777 if (isTerminating) {
36778 break;
36779 }
36780 }
36781 }
36782 }
36783 } else {
36784 close(fd);
36785 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
36786 return MA_NO_BACKEND;
36787 }
36788
36789 close(fd);
36790 return MA_SUCCESS;
36791 }
36792
36793 static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
36794 {
36795 unsigned int minChannels;
36796 unsigned int maxChannels;
36797 unsigned int iRate;
36798
36799 MA_ASSERT(pContext != NULL);
36800 MA_ASSERT(pAudioInfo != NULL);
36801 MA_ASSERT(pDeviceInfo != NULL);
36802
36803 /* If we support all channels we just report 0. */
36804 minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
36805 maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
36806
36807 /*
36808 OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,
36809 which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which
36810 case we'll need to use min_rate and max_rate and report only standard rates.
36811 */
36812 if (pAudioInfo->nrates > 0) {
36813 for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
36814 unsigned int rate = pAudioInfo->rates[iRate];
36815
36816 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
36817 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. */
36818 } else {
36819 unsigned int iChannel;
36820 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
36821 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
36822 }
36823 }
36824 }
36825 } else {
36826 for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
36827 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
36828
36829 if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
36830 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
36831 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. */
36832 } else {
36833 unsigned int iChannel;
36834 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
36835 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
36836 }
36837 }
36838 }
36839 }
36840 }
36841 }
36842
36843 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)
36844 {
36845 ma_bool32 foundDevice;
36846 int fdTemp;
36847 oss_sysinfo si;
36848 int result;
36849
36850 MA_ASSERT(pContext != NULL);
36851
36852 /* Handle the default device a little differently. */
36853 if (pDeviceID == NULL) {
36854 if (deviceType == ma_device_type_playback) {
36855 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
36856 } else {
36857 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
36858 }
36859
36860 return MA_SUCCESS;
36861 }
36862
36863
36864 /* If we get here it means we are _not_ using the default device. */
36865 foundDevice = MA_FALSE;
36866
36867 fdTemp = ma_open_temp_device__oss();
36868 if (fdTemp == -1) {
36869 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.");
36870 return MA_NO_BACKEND;
36871 }
36872
36873 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
36874 if (result != -1) {
36875 int iAudioDevice;
36876 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
36877 oss_audioinfo ai;
36878 ai.dev = iAudioDevice;
36879 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
36880 if (result != -1) {
36881 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
36882 /* It has the same name, so now just confirm the type. */
36883 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
36884 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
36885 unsigned int formatMask;
36886
36887 /* ID */
36888 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
36889
36890 /*
36891 The human readable device name should be in the "ai.handle" variable, but it can
36892 sometimes be empty in which case we just fall back to "ai.name" which is less user
36893 friendly, but usually has a value.
36894 */
36895 if (ai.handle[0] != '\0') {
36896 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
36897 } else {
36898 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
36899 }
36900
36901
36902 pDeviceInfo->nativeDataFormatCount = 0;
36903
36904 if (deviceType == ma_device_type_playback) {
36905 formatMask = ai.oformats;
36906 } else {
36907 formatMask = ai.iformats;
36908 }
36909
36910 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
36911 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
36912 }
36913 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
36914 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
36915 }
36916 if ((formatMask & AFMT_U8) != 0) {
36917 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
36918 }
36919
36920 foundDevice = MA_TRUE;
36921 break;
36922 }
36923 }
36924 }
36925 }
36926 } else {
36927 close(fdTemp);
36928 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
36929 return MA_NO_BACKEND;
36930 }
36931
36932
36933 close(fdTemp);
36934
36935 if (!foundDevice) {
36936 return MA_NO_DEVICE;
36937 }
36938
36939 return MA_SUCCESS;
36940 }
36941
36942 static ma_result ma_device_uninit__oss(ma_device* pDevice)
36943 {
36944 MA_ASSERT(pDevice != NULL);
36945
36946 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36947 close(pDevice->oss.fdCapture);
36948 }
36949
36950 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36951 close(pDevice->oss.fdPlayback);
36952 }
36953
36954 return MA_SUCCESS;
36955 }
36956
36957 static int ma_format_to_oss(ma_format format)
36958 {
36959 int ossFormat = AFMT_U8;
36960 switch (format) {
36961 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
36962 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
36963 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
36964 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
36965 case ma_format_u8:
36966 default: ossFormat = AFMT_U8; break;
36967 }
36968
36969 return ossFormat;
36970 }
36971
36972 static ma_format ma_format_from_oss(int ossFormat)
36973 {
36974 if (ossFormat == AFMT_U8) {
36975 return ma_format_u8;
36976 } else {
36977 if (ma_is_little_endian()) {
36978 switch (ossFormat) {
36979 case AFMT_S16_LE: return ma_format_s16;
36980 case AFMT_S32_LE: return ma_format_s32;
36981 default: return ma_format_unknown;
36982 }
36983 } else {
36984 switch (ossFormat) {
36985 case AFMT_S16_BE: return ma_format_s16;
36986 case AFMT_S32_BE: return ma_format_s32;
36987 default: return ma_format_unknown;
36988 }
36989 }
36990 }
36991
36992 return ma_format_unknown;
36993 }
36994
36995 static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
36996 {
36997 ma_result result;
36998 int ossResult;
36999 int fd;
37000 const ma_device_id* pDeviceID = NULL;
37001 ma_share_mode shareMode;
37002 int ossFormat;
37003 int ossChannels;
37004 int ossSampleRate;
37005 int ossFragment;
37006
37007 MA_ASSERT(pDevice != NULL);
37008 MA_ASSERT(pConfig != NULL);
37009 MA_ASSERT(deviceType != ma_device_type_duplex);
37010
37011 pDeviceID = pDescriptor->pDeviceID;
37012 shareMode = pDescriptor->shareMode;
37013 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. */
37014 ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
37015 ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
37016
37017 result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);
37018 if (result != MA_SUCCESS) {
37019 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
37020 return result;
37021 }
37022
37023 /*
37024 The OSS documantation is very clear about the order we should be initializing the device's properties:
37025 1) Format
37026 2) Channels
37027 3) Sample rate.
37028 */
37029
37030 /* Format. */
37031 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
37032 if (ossResult == -1) {
37033 close(fd);
37034 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.");
37035 return ma_result_from_errno(errno);
37036 }
37037
37038 /* Channels. */
37039 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
37040 if (ossResult == -1) {
37041 close(fd);
37042 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.");
37043 return ma_result_from_errno(errno);
37044 }
37045
37046 /* Sample Rate. */
37047 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
37048 if (ossResult == -1) {
37049 close(fd);
37050 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.");
37051 return ma_result_from_errno(errno);
37052 }
37053
37054 /*
37055 Buffer.
37056
37057 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
37058 it should be done before or after format/channels/rate.
37059
37060 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
37061 value.
37062 */
37063 {
37064 ma_uint32 periodSizeInFrames;
37065 ma_uint32 periodSizeInBytes;
37066 ma_uint32 ossFragmentSizePower;
37067
37068 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);
37069
37070 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
37071 if (periodSizeInBytes < 16) {
37072 periodSizeInBytes = 16;
37073 }
37074
37075 ossFragmentSizePower = 4;
37076 periodSizeInBytes >>= 4;
37077 while (periodSizeInBytes >>= 1) {
37078 ossFragmentSizePower += 1;
37079 }
37080
37081 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
37082 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
37083 if (ossResult == -1) {
37084 close(fd);
37085 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.");
37086 return ma_result_from_errno(errno);
37087 }
37088 }
37089
37090 /* Internal settings. */
37091 if (deviceType == ma_device_type_capture) {
37092 pDevice->oss.fdCapture = fd;
37093 } else {
37094 pDevice->oss.fdPlayback = fd;
37095 }
37096
37097 pDescriptor->format = ma_format_from_oss(ossFormat);
37098 pDescriptor->channels = ossChannels;
37099 pDescriptor->sampleRate = ossSampleRate;
37100 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
37101 pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16);
37102 pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);
37103
37104 if (pDescriptor->format == ma_format_unknown) {
37105 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.");
37106 return MA_FORMAT_NOT_SUPPORTED;
37107 }
37108
37109 return MA_SUCCESS;
37110 }
37111
37112 static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
37113 {
37114 MA_ASSERT(pDevice != NULL);
37115 MA_ASSERT(pConfig != NULL);
37116
37117 MA_ZERO_OBJECT(&pDevice->oss);
37118
37119 if (pConfig->deviceType == ma_device_type_loopback) {
37120 return MA_DEVICE_TYPE_NOT_SUPPORTED;
37121 }
37122
37123 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
37124 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
37125 if (result != MA_SUCCESS) {
37126 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
37127 return result;
37128 }
37129 }
37130
37131 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
37132 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
37133 if (result != MA_SUCCESS) {
37134 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
37135 return result;
37136 }
37137 }
37138
37139 return MA_SUCCESS;
37140 }
37141
37142 /*
37143 Note on Starting and Stopping
37144 =============================
37145 In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when
37146 trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will
37147 fail. Instead what we need to do is just not write or read to and from the device when the
37148 device is not running.
37149
37150 As a result, both the start and stop functions for OSS are just empty stubs. The starting and
37151 stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check
37152 the device state, and if the device is stopped they will simply not do any kind of processing.
37153
37154 The downside to this technique is that I've noticed a fairly lengthy delay in stopping the
37155 device, up to a second. This is on a virtual machine, and as such might just be due to the
37156 virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for
37157 the moment that's just how it's going to have to be.
37158
37159 When starting the device, OSS will automatically start it when write() or read() is called.
37160 */
37161 static ma_result ma_device_start__oss(ma_device* pDevice)
37162 {
37163 MA_ASSERT(pDevice != NULL);
37164
37165 /* The device is automatically started with reading and writing. */
37166 (void)pDevice;
37167
37168 return MA_SUCCESS;
37169 }
37170
37171 static ma_result ma_device_stop__oss(ma_device* pDevice)
37172 {
37173 MA_ASSERT(pDevice != NULL);
37174
37175 /* See note above on why this is empty. */
37176 (void)pDevice;
37177
37178 return MA_SUCCESS;
37179 }
37180
37181 static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
37182 {
37183 int resultOSS;
37184 ma_uint32 deviceState;
37185
37186 if (pFramesWritten != NULL) {
37187 *pFramesWritten = 0;
37188 }
37189
37190 /* Don't do any processing if the device is stopped. */
37191 deviceState = ma_device_get_state(pDevice);
37192 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
37193 return MA_SUCCESS;
37194 }
37195
37196 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
37197 if (resultOSS < 0) {
37198 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.");
37199 return ma_result_from_errno(errno);
37200 }
37201
37202 if (pFramesWritten != NULL) {
37203 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
37204 }
37205
37206 return MA_SUCCESS;
37207 }
37208
37209 static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
37210 {
37211 int resultOSS;
37212 ma_uint32 deviceState;
37213
37214 if (pFramesRead != NULL) {
37215 *pFramesRead = 0;
37216 }
37217
37218 /* Don't do any processing if the device is stopped. */
37219 deviceState = ma_device_get_state(pDevice);
37220 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
37221 return MA_SUCCESS;
37222 }
37223
37224 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
37225 if (resultOSS < 0) {
37226 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.");
37227 return ma_result_from_errno(errno);
37228 }
37229
37230 if (pFramesRead != NULL) {
37231 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
37232 }
37233
37234 return MA_SUCCESS;
37235 }
37236
37237 static ma_result ma_context_uninit__oss(ma_context* pContext)
37238 {
37239 MA_ASSERT(pContext != NULL);
37240 MA_ASSERT(pContext->backend == ma_backend_oss);
37241
37242 (void)pContext;
37243 return MA_SUCCESS;
37244 }
37245
37246 static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
37247 {
37248 int fd;
37249 int ossVersion;
37250 int result;
37251
37252 MA_ASSERT(pContext != NULL);
37253
37254 (void)pConfig;
37255
37256 /* Try opening a temporary device first so we can get version information. This is closed at the end. */
37257 fd = ma_open_temp_device__oss();
37258 if (fd == -1) {
37259 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. */
37260 return MA_NO_BACKEND;
37261 }
37262
37263 /* Grab the OSS version. */
37264 ossVersion = 0;
37265 result = ioctl(fd, OSS_GETVERSION, &ossVersion);
37266 if (result == -1) {
37267 close(fd);
37268 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.");
37269 return MA_NO_BACKEND;
37270 }
37271
37272 /* The file handle to temp device is no longer needed. Close ASAP. */
37273 close(fd);
37274
37275 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
37276 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
37277
37278 pCallbacks->onContextInit = ma_context_init__oss;
37279 pCallbacks->onContextUninit = ma_context_uninit__oss;
37280 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
37281 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
37282 pCallbacks->onDeviceInit = ma_device_init__oss;
37283 pCallbacks->onDeviceUninit = ma_device_uninit__oss;
37284 pCallbacks->onDeviceStart = ma_device_start__oss;
37285 pCallbacks->onDeviceStop = ma_device_stop__oss;
37286 pCallbacks->onDeviceRead = ma_device_read__oss;
37287 pCallbacks->onDeviceWrite = ma_device_write__oss;
37288 pCallbacks->onDeviceDataLoop = NULL;
37289
37290 return MA_SUCCESS;
37291 }
37292 #endif /* OSS */
37293
37294
37295
37296
37297
37298 /******************************************************************************
37299
37300 AAudio Backend
37301
37302 ******************************************************************************/
37303 #ifdef MA_HAS_AAUDIO
37304
37305 /*#include <AAudio/AAudio.h>*/
37306
37307 typedef int32_t ma_aaudio_result_t;
37308 typedef int32_t ma_aaudio_direction_t;
37309 typedef int32_t ma_aaudio_sharing_mode_t;
37310 typedef int32_t ma_aaudio_format_t;
37311 typedef int32_t ma_aaudio_stream_state_t;
37312 typedef int32_t ma_aaudio_performance_mode_t;
37313 typedef int32_t ma_aaudio_usage_t;
37314 typedef int32_t ma_aaudio_content_type_t;
37315 typedef int32_t ma_aaudio_input_preset_t;
37316 typedef int32_t ma_aaudio_allowed_capture_policy_t;
37317 typedef int32_t ma_aaudio_data_callback_result_t;
37318 typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
37319 typedef struct ma_AAudioStream_t* ma_AAudioStream;
37320
37321 #define MA_AAUDIO_UNSPECIFIED 0
37322
37323 /* Result codes. miniaudio only cares about the success code. */
37324 #define MA_AAUDIO_OK 0
37325
37326 /* Directions. */
37327 #define MA_AAUDIO_DIRECTION_OUTPUT 0
37328 #define MA_AAUDIO_DIRECTION_INPUT 1
37329
37330 /* Sharing modes. */
37331 #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
37332 #define MA_AAUDIO_SHARING_MODE_SHARED 1
37333
37334 /* Formats. */
37335 #define MA_AAUDIO_FORMAT_PCM_I16 1
37336 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2
37337
37338 /* Stream states. */
37339 #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
37340 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
37341 #define MA_AAUDIO_STREAM_STATE_OPEN 2
37342 #define MA_AAUDIO_STREAM_STATE_STARTING 3
37343 #define MA_AAUDIO_STREAM_STATE_STARTED 4
37344 #define MA_AAUDIO_STREAM_STATE_PAUSING 5
37345 #define MA_AAUDIO_STREAM_STATE_PAUSED 6
37346 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7
37347 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8
37348 #define MA_AAUDIO_STREAM_STATE_STOPPING 9
37349 #define MA_AAUDIO_STREAM_STATE_STOPPED 10
37350 #define MA_AAUDIO_STREAM_STATE_CLOSING 11
37351 #define MA_AAUDIO_STREAM_STATE_CLOSED 12
37352 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
37353
37354 /* Performance modes. */
37355 #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
37356 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
37357 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
37358
37359 /* Usage types. */
37360 #define MA_AAUDIO_USAGE_MEDIA 1
37361 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
37362 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
37363 #define MA_AAUDIO_USAGE_ALARM 4
37364 #define MA_AAUDIO_USAGE_NOTIFICATION 5
37365 #define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
37366 #define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
37367 #define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
37368 #define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
37369 #define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
37370 #define MA_AAUDIO_USAGE_GAME 14
37371 #define MA_AAUDIO_USAGE_ASSISTANT 16
37372 #define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
37373 #define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
37374 #define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
37375 #define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
37376
37377 /* Content types. */
37378 #define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
37379 #define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
37380 #define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
37381 #define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
37382
37383 /* Input presets. */
37384 #define MA_AAUDIO_INPUT_PRESET_GENERIC 1
37385 #define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
37386 #define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
37387 #define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
37388 #define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
37389 #define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
37390
37391 /* Allowed Capture Policies */
37392 #define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1
37393 #define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2
37394 #define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3
37395
37396 /* Callback results. */
37397 #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
37398 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1
37399
37400
37401 typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
37402 typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
37403
37404 typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
37405 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
37406 typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
37407 typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
37408 typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
37409 typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
37410 typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
37411 typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
37412 typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
37413 typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
37414 typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
37415 typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
37416 typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
37417 typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
37418 typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
37419 typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
37420 typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy);
37421 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
37422 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
37423 typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
37424 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);
37425 typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
37426 typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
37427 typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
37428 typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
37429 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
37430 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
37431 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
37432 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
37433
37434 static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
37435 {
37436 switch (resultAA)
37437 {
37438 case MA_AAUDIO_OK: return MA_SUCCESS;
37439 default: break;
37440 }
37441
37442 return MA_ERROR;
37443 }
37444
37445 static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
37446 {
37447 switch (usage) {
37448 case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA;
37449 case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
37450 case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
37451 case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM;
37452 case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION;
37453 case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
37454 case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
37455 case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
37456 case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
37457 case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
37458 case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME;
37459 case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT;
37460 case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
37461 case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
37462 case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
37463 case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
37464 default: break;
37465 }
37466
37467 return MA_AAUDIO_USAGE_MEDIA;
37468 }
37469
37470 static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
37471 {
37472 switch (contentType) {
37473 case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
37474 case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
37475 case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
37476 case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
37477 default: break;
37478 }
37479
37480 return MA_AAUDIO_CONTENT_TYPE_SPEECH;
37481 }
37482
37483 static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
37484 {
37485 switch (inputPreset) {
37486 case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
37487 case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
37488 case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
37489 case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
37490 case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
37491 case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
37492 default: break;
37493 }
37494
37495 return MA_AAUDIO_INPUT_PRESET_GENERIC;
37496 }
37497
37498 static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy)
37499 {
37500 switch (allowedCapturePolicy) {
37501 case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
37502 case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM;
37503 case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE;
37504 default: break;
37505 }
37506
37507 return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
37508 }
37509
37510 static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
37511 {
37512 ma_result result;
37513 ma_job job;
37514 ma_device* pDevice = (ma_device*)pUserData;
37515 MA_ASSERT(pDevice != NULL);
37516
37517 (void)error;
37518
37519 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));
37520
37521 /*
37522 When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation,
37523 we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this
37524 cleanly and safely.
37525 */
37526 job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE);
37527 job.data.device.aaudio.reroute.pDevice = pDevice;
37528
37529 if (pStream == pDevice->aaudio.pStreamCapture) {
37530 job.data.device.aaudio.reroute.deviceType = ma_device_type_capture;
37531 }
37532 else {
37533 job.data.device.aaudio.reroute.deviceType = ma_device_type_playback;
37534 }
37535
37536 result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);
37537 if (result != MA_SUCCESS) {
37538 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n");
37539 return;
37540 }
37541 }
37542
37543 static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
37544 {
37545 ma_device* pDevice = (ma_device*)pUserData;
37546 MA_ASSERT(pDevice != NULL);
37547
37548 ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
37549
37550 (void)pStream;
37551 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
37552 }
37553
37554 static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
37555 {
37556 ma_device* pDevice = (ma_device*)pUserData;
37557 MA_ASSERT(pDevice != NULL);
37558
37559 ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
37560
37561 (void)pStream;
37562 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
37563 }
37564
37565 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)
37566 {
37567 ma_AAudioStreamBuilder* pBuilder;
37568 ma_aaudio_result_t resultAA;
37569
37570 /* Safety. */
37571 *ppBuilder = NULL;
37572
37573 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
37574 if (resultAA != MA_AAUDIO_OK) {
37575 return ma_result_from_aaudio(resultAA);
37576 }
37577
37578 if (pDeviceID != NULL) {
37579 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
37580 }
37581
37582 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
37583 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
37584
37585
37586 /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
37587 if (pDescriptor != NULL) {
37588 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. */
37589
37590 if (pDescriptor->sampleRate != 0) {
37591 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
37592 }
37593
37594 if (deviceType == ma_device_type_capture) {
37595 if (pDescriptor->channels != 0) {
37596 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
37597 }
37598 if (pDescriptor->format != ma_format_unknown) {
37599 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
37600 }
37601 } else {
37602 if (pDescriptor->channels != 0) {
37603 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
37604 }
37605 if (pDescriptor->format != ma_format_unknown) {
37606 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
37607 }
37608 }
37609
37610
37611 /*
37612 There have been reports where setting the frames per data callback results in an error
37613 later on from Android. To address this, I'm experimenting with simply not setting it on
37614 anything from Android 11 and earlier. Suggestions welcome on how we might be able to make
37615 this more targetted.
37616 */
37617 if (pConfig->aaudio.enableCompatibilityWorkarounds && ma_android_sdk_version() > 30) {
37618 /*
37619 AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you
37620 retrieve the actual sample rate until after you've opened the stream. But you need to configure
37621 the buffer capacity before you open the stream... :/
37622
37623 To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.
37624 */
37625 ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
37626
37627 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
37628 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
37629 }
37630
37631 if (deviceType == ma_device_type_capture) {
37632 if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
37633 ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
37634 }
37635
37636 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
37637 } else {
37638 if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
37639 ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
37640 }
37641
37642 if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
37643 ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
37644 }
37645
37646 if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) {
37647 ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy));
37648 }
37649
37650 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
37651 }
37652
37653 /* 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. */
37654 ((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);
37655
37656 /* We need to set an error callback to detect device changes. */
37657 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. */
37658 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
37659 }
37660 }
37661
37662 *ppBuilder = pBuilder;
37663
37664 return MA_SUCCESS;
37665 }
37666
37667 static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)
37668 {
37669 ma_result result;
37670
37671 result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));
37672 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
37673
37674 return result;
37675 }
37676
37677 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)
37678 {
37679 ma_result result;
37680 ma_AAudioStreamBuilder* pBuilder;
37681
37682 *ppStream = NULL;
37683
37684 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);
37685 if (result != MA_SUCCESS) {
37686 return result;
37687 }
37688
37689 return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
37690 }
37691
37692 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)
37693 {
37694 ma_result result;
37695 ma_AAudioStreamBuilder* pBuilder;
37696
37697 MA_ASSERT(pDevice != NULL);
37698 MA_ASSERT(pDescriptor != NULL);
37699 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
37700
37701 *ppStream = NULL;
37702
37703 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);
37704 if (result != MA_SUCCESS) {
37705 return result;
37706 }
37707
37708 return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);
37709 }
37710
37711 static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
37712 {
37713 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
37714 }
37715
37716 static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
37717 {
37718 /* The only way to know this is to try creating a stream. */
37719 ma_AAudioStream* pStream;
37720 ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);
37721 if (result != MA_SUCCESS) {
37722 return MA_FALSE;
37723 }
37724
37725 ma_close_stream__aaudio(pContext, pStream);
37726 return MA_TRUE;
37727 }
37728
37729 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)
37730 {
37731 ma_aaudio_stream_state_t actualNewState;
37732 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
37733 if (resultAA != MA_AAUDIO_OK) {
37734 return ma_result_from_aaudio(resultAA);
37735 }
37736
37737 if (newState != actualNewState) {
37738 return MA_ERROR; /* Failed to transition into the expected state. */
37739 }
37740
37741 return MA_SUCCESS;
37742 }
37743
37744
37745 static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
37746 {
37747 ma_bool32 cbResult = MA_TRUE;
37748
37749 MA_ASSERT(pContext != NULL);
37750 MA_ASSERT(callback != NULL);
37751
37752 /* 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. */
37753
37754 /* Playback. */
37755 if (cbResult) {
37756 ma_device_info deviceInfo;
37757 MA_ZERO_OBJECT(&deviceInfo);
37758 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
37759 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
37760
37761 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
37762 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
37763 }
37764 }
37765
37766 /* Capture. */
37767 if (cbResult) {
37768 ma_device_info deviceInfo;
37769 MA_ZERO_OBJECT(&deviceInfo);
37770 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
37771 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
37772
37773 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
37774 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
37775 }
37776 }
37777
37778 return MA_SUCCESS;
37779 }
37780
37781 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)
37782 {
37783 MA_ASSERT(pContext != NULL);
37784 MA_ASSERT(pStream != NULL);
37785 MA_ASSERT(pDeviceInfo != NULL);
37786
37787 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
37788 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
37789 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
37790 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
37791 pDeviceInfo->nativeDataFormatCount += 1;
37792 }
37793
37794 static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)
37795 {
37796 /* AAudio supports s16 and f32. */
37797 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);
37798 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);
37799 }
37800
37801 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)
37802 {
37803 ma_AAudioStream* pStream;
37804 ma_result result;
37805
37806 MA_ASSERT(pContext != NULL);
37807
37808 /* ID */
37809 if (pDeviceID != NULL) {
37810 pDeviceInfo->id.aaudio = pDeviceID->aaudio;
37811 } else {
37812 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
37813 }
37814
37815 /* Name */
37816 if (deviceType == ma_device_type_playback) {
37817 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
37818 } else {
37819 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
37820 }
37821
37822
37823 pDeviceInfo->nativeDataFormatCount = 0;
37824
37825 /* We'll need to open the device to get accurate sample rate and channel count information. */
37826 result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);
37827 if (result != MA_SUCCESS) {
37828 return result;
37829 }
37830
37831 ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);
37832
37833 ma_close_stream__aaudio(pContext, pStream);
37834 pStream = NULL;
37835
37836 return MA_SUCCESS;
37837 }
37838
37839
37840 static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
37841 {
37842 MA_ASSERT(pDevice != NULL);
37843
37844 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37845 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
37846 pDevice->aaudio.pStreamCapture = NULL;
37847 }
37848
37849 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37850 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
37851 pDevice->aaudio.pStreamPlayback = NULL;
37852 }
37853
37854 return MA_SUCCESS;
37855 }
37856
37857 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)
37858 {
37859 ma_result result;
37860 int32_t bufferCapacityInFrames;
37861 int32_t framesPerDataCallback;
37862 ma_AAudioStream* pStream;
37863
37864 MA_ASSERT(pDevice != NULL);
37865 MA_ASSERT(pConfig != NULL);
37866 MA_ASSERT(pDescriptor != NULL);
37867
37868 *ppStream = NULL; /* Safety. */
37869
37870 /* First step is to open the stream. From there we'll be able to extract the internal configuration. */
37871 result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);
37872 if (result != MA_SUCCESS) {
37873 return result; /* Failed to open the AAudio stream. */
37874 }
37875
37876 /* Now extract the internal configuration. */
37877 pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
37878 pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);
37879 pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);
37880
37881 /* For the channel map we need to be sure we don't overflow any buffers. */
37882 if (pDescriptor->channels <= MA_MAX_CHANNELS) {
37883 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. */
37884 } else {
37885 ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */
37886 }
37887
37888 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);
37889 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);
37890
37891 if (framesPerDataCallback > 0) {
37892 pDescriptor->periodSizeInFrames = framesPerDataCallback;
37893 pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback;
37894 } else {
37895 pDescriptor->periodSizeInFrames = bufferCapacityInFrames;
37896 pDescriptor->periodCount = 1;
37897 }
37898
37899 *ppStream = pStream;
37900
37901 return MA_SUCCESS;
37902 }
37903
37904 static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
37905 {
37906 ma_result result;
37907
37908 MA_ASSERT(pDevice != NULL);
37909
37910 if (pConfig->deviceType == ma_device_type_loopback) {
37911 return MA_DEVICE_TYPE_NOT_SUPPORTED;
37912 }
37913
37914 pDevice->aaudio.usage = pConfig->aaudio.usage;
37915 pDevice->aaudio.contentType = pConfig->aaudio.contentType;
37916 pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset;
37917 pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy;
37918 pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute;
37919
37920 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
37921 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
37922 if (result != MA_SUCCESS) {
37923 return result;
37924 }
37925 }
37926
37927 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
37928 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
37929 if (result != MA_SUCCESS) {
37930 return result;
37931 }
37932 }
37933
37934 return MA_SUCCESS;
37935 }
37936
37937 static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
37938 {
37939 ma_aaudio_result_t resultAA;
37940 ma_aaudio_stream_state_t currentState;
37941
37942 MA_ASSERT(pDevice != NULL);
37943
37944 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
37945 if (resultAA != MA_AAUDIO_OK) {
37946 return ma_result_from_aaudio(resultAA);
37947 }
37948
37949 /* Do we actually need to wait for the device to transition into it's started state? */
37950
37951 /* 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. */
37952 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
37953 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
37954 ma_result result;
37955
37956 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
37957 return MA_ERROR; /* Expecting the stream to be a starting or started state. */
37958 }
37959
37960 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
37961 if (result != MA_SUCCESS) {
37962 return result;
37963 }
37964 }
37965
37966 return MA_SUCCESS;
37967 }
37968
37969 static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
37970 {
37971 ma_aaudio_result_t resultAA;
37972 ma_aaudio_stream_state_t currentState;
37973
37974 MA_ASSERT(pDevice != NULL);
37975
37976 /*
37977 From the AAudio documentation:
37978
37979 The stream will stop after all of the data currently buffered has been played.
37980
37981 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
37982 */
37983 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
37984 if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
37985 return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */
37986 }
37987
37988 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
37989 if (resultAA != MA_AAUDIO_OK) {
37990 return ma_result_from_aaudio(resultAA);
37991 }
37992
37993 /* 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. */
37994 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
37995 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
37996 ma_result result;
37997
37998 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
37999 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
38000 }
38001
38002 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
38003 if (result != MA_SUCCESS) {
38004 return result;
38005 }
38006 }
38007
38008 return MA_SUCCESS;
38009 }
38010
38011 static ma_result ma_device_start__aaudio(ma_device* pDevice)
38012 {
38013 MA_ASSERT(pDevice != NULL);
38014
38015 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38016 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38017 if (result != MA_SUCCESS) {
38018 return result;
38019 }
38020 }
38021
38022 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38023 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
38024 if (result != MA_SUCCESS) {
38025 if (pDevice->type == ma_device_type_duplex) {
38026 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38027 }
38028 return result;
38029 }
38030 }
38031
38032 return MA_SUCCESS;
38033 }
38034
38035 static ma_result ma_device_stop__aaudio(ma_device* pDevice)
38036 {
38037 MA_ASSERT(pDevice != NULL);
38038
38039 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38040 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38041 if (result != MA_SUCCESS) {
38042 return result;
38043 }
38044 }
38045
38046 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38047 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
38048 if (result != MA_SUCCESS) {
38049 return result;
38050 }
38051 }
38052
38053 ma_device__on_notification_stopped(pDevice);
38054
38055 return MA_SUCCESS;
38056 }
38057
38058 static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType)
38059 {
38060 ma_result result;
38061
38062 MA_ASSERT(pDevice != NULL);
38063
38064 /* The first thing to do is close the streams. */
38065 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
38066 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
38067 pDevice->aaudio.pStreamCapture = NULL;
38068 }
38069
38070 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38071 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
38072 pDevice->aaudio.pStreamPlayback = NULL;
38073 }
38074
38075 /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */
38076 {
38077 ma_device_config deviceConfig;
38078 ma_device_descriptor descriptorPlayback;
38079 ma_device_descriptor descriptorCapture;
38080
38081 deviceConfig = ma_device_config_init(deviceType);
38082 deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */
38083 deviceConfig.playback.shareMode = pDevice->playback.shareMode;
38084 deviceConfig.playback.format = pDevice->playback.format;
38085 deviceConfig.playback.channels = pDevice->playback.channels;
38086 deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */
38087 deviceConfig.capture.shareMode = pDevice->capture.shareMode;
38088 deviceConfig.capture.format = pDevice->capture.format;
38089 deviceConfig.capture.channels = pDevice->capture.channels;
38090 deviceConfig.sampleRate = pDevice->sampleRate;
38091 deviceConfig.aaudio.usage = pDevice->aaudio.usage;
38092 deviceConfig.aaudio.contentType = pDevice->aaudio.contentType;
38093 deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset;
38094 deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy;
38095 deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute;
38096 deviceConfig.periods = 1;
38097
38098 /* Try to get an accurate period size. */
38099 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38100 deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
38101 } else {
38102 deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
38103 }
38104
38105 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
38106 descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID;
38107 descriptorCapture.shareMode = deviceConfig.capture.shareMode;
38108 descriptorCapture.format = deviceConfig.capture.format;
38109 descriptorCapture.channels = deviceConfig.capture.channels;
38110 descriptorCapture.sampleRate = deviceConfig.sampleRate;
38111 descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames;
38112 descriptorCapture.periodCount = deviceConfig.periods;
38113 }
38114
38115 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
38116 descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID;
38117 descriptorPlayback.shareMode = deviceConfig.playback.shareMode;
38118 descriptorPlayback.format = deviceConfig.playback.format;
38119 descriptorPlayback.channels = deviceConfig.playback.channels;
38120 descriptorPlayback.sampleRate = deviceConfig.sampleRate;
38121 descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames;
38122 descriptorPlayback.periodCount = deviceConfig.periods;
38123 }
38124
38125 result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);
38126 if (result != MA_SUCCESS) {
38127 return result;
38128 }
38129
38130 result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);
38131 if (result != MA_SUCCESS) {
38132 ma_device_uninit__aaudio(pDevice);
38133 return result;
38134 }
38135
38136 /* We'll only ever do this in response to a reroute. */
38137 ma_device__on_notification_rerouted(pDevice);
38138
38139 /* If the device is started, start the streams. Maybe make this configurable? */
38140 if (ma_device_get_state(pDevice) == ma_device_state_started) {
38141 if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {
38142 ma_device_start__aaudio(pDevice);
38143 } else {
38144 ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */
38145 }
38146 }
38147
38148 return MA_SUCCESS;
38149 }
38150 }
38151
38152 static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
38153 {
38154 ma_AAudioStream* pStream = NULL;
38155
38156 MA_ASSERT(pDevice != NULL);
38157 MA_ASSERT(type != ma_device_type_duplex);
38158 MA_ASSERT(pDeviceInfo != NULL);
38159
38160 if (type == ma_device_type_playback) {
38161 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;
38162 pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;
38163 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
38164 }
38165 if (type == ma_device_type_capture) {
38166 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;
38167 pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;
38168 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
38169 }
38170
38171 /* Safety. Should never happen. */
38172 if (pStream == NULL) {
38173 return MA_INVALID_OPERATION;
38174 }
38175
38176 pDeviceInfo->nativeDataFormatCount = 0;
38177 ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo);
38178
38179 return MA_SUCCESS;
38180 }
38181
38182
38183 static ma_result ma_context_uninit__aaudio(ma_context* pContext)
38184 {
38185 MA_ASSERT(pContext != NULL);
38186 MA_ASSERT(pContext->backend == ma_backend_aaudio);
38187
38188 ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks);
38189
38190 ma_dlclose(pContext, pContext->aaudio.hAAudio);
38191 pContext->aaudio.hAAudio = NULL;
38192
38193 return MA_SUCCESS;
38194 }
38195
38196 static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
38197 {
38198 size_t i;
38199 const char* libNames[] = {
38200 "libaaudio.so"
38201 };
38202
38203 for (i = 0; i < ma_countof(libNames); ++i) {
38204 pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
38205 if (pContext->aaudio.hAAudio != NULL) {
38206 break;
38207 }
38208 }
38209
38210 if (pContext->aaudio.hAAudio == NULL) {
38211 return MA_FAILED_TO_INIT_BACKEND;
38212 }
38213
38214 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
38215 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
38216 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
38217 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
38218 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
38219 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
38220 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
38221 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
38222 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
38223 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
38224 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
38225 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
38226 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
38227 pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
38228 pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
38229 pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
38230 pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy");
38231 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
38232 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
38233 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
38234 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
38235 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
38236 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
38237 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
38238 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
38239 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
38240 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
38241 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
38242 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
38243
38244
38245 pCallbacks->onContextInit = ma_context_init__aaudio;
38246 pCallbacks->onContextUninit = ma_context_uninit__aaudio;
38247 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
38248 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
38249 pCallbacks->onDeviceInit = ma_device_init__aaudio;
38250 pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
38251 pCallbacks->onDeviceStart = ma_device_start__aaudio;
38252 pCallbacks->onDeviceStop = ma_device_stop__aaudio;
38253 pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */
38254 pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */
38255 pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */
38256 pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio;
38257
38258
38259 /* We need a job thread so we can deal with rerouting. */
38260 {
38261 ma_result result;
38262 ma_device_job_thread_config jobThreadConfig;
38263
38264 jobThreadConfig = ma_device_job_thread_config_init();
38265
38266 result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);
38267 if (result != MA_SUCCESS) {
38268 ma_dlclose(pContext, pContext->aaudio.hAAudio);
38269 pContext->aaudio.hAAudio = NULL;
38270 return result;
38271 }
38272 }
38273
38274
38275 (void)pConfig;
38276 return MA_SUCCESS;
38277 }
38278
38279 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
38280 {
38281 ma_device* pDevice;
38282
38283 MA_ASSERT(pJob != NULL);
38284
38285 pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;
38286 MA_ASSERT(pDevice != NULL);
38287
38288 /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */
38289 return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
38290 }
38291 #else
38292 /* Getting here means there is no AAudio backend so we need a no-op job implementation. */
38293 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
38294 {
38295 return ma_job_process__noop(pJob);
38296 }
38297 #endif /* AAudio */
38298
38299
38300 /******************************************************************************
38301
38302 OpenSL|ES Backend
38303
38304 ******************************************************************************/
38305 #ifdef MA_HAS_OPENSL
38306 #include <SLES/OpenSLES.h>
38307 #ifdef MA_ANDROID
38308 #include <SLES/OpenSLES_Android.h>
38309 #endif
38310
38311 typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
38312
38313 /* OpenSL|ES has one-per-application objects :( */
38314 static SLObjectItf g_maEngineObjectSL = NULL;
38315 static SLEngineItf g_maEngineSL = NULL;
38316 static ma_uint32 g_maOpenSLInitCounter = 0;
38317 static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
38318
38319 #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
38320 #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
38321 #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
38322 #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
38323
38324 #ifdef MA_ANDROID
38325 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
38326 #else
38327 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
38328 #endif
38329
38330 static ma_result ma_result_from_OpenSL(SLuint32 result)
38331 {
38332 switch (result)
38333 {
38334 case SL_RESULT_SUCCESS: return MA_SUCCESS;
38335 case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
38336 case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
38337 case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
38338 case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
38339 case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
38340 case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
38341 case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
38342 case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
38343 case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
38344 case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
38345 case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
38346 case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
38347 case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
38348 case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
38349 case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
38350 case SL_RESULT_CONTROL_LOST: return MA_ERROR;
38351 default: return MA_ERROR;
38352 }
38353 }
38354
38355 /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
38356 static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
38357 {
38358 switch (id)
38359 {
38360 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
38361 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
38362 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
38363 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
38364 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
38365 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
38366 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
38367 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
38368 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
38369 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
38370 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
38371 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
38372 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
38373 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
38374 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
38375 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
38376 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
38377 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
38378 default: return 0;
38379 }
38380 }
38381
38382 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
38383 static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
38384 {
38385 switch (id)
38386 {
38387 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
38388 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
38389 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
38390 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
38391 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
38392 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
38393 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
38394 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
38395 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
38396 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
38397 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
38398 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
38399 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
38400 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
38401 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
38402 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
38403 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
38404 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
38405 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
38406 default: return 0;
38407 }
38408 }
38409
38410 /* Converts a channel mapping to an OpenSL-style channel mask. */
38411 static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
38412 {
38413 SLuint32 channelMask = 0;
38414 ma_uint32 iChannel;
38415 for (iChannel = 0; iChannel < channels; ++iChannel) {
38416 channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
38417 }
38418
38419 return channelMask;
38420 }
38421
38422 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */
38423 static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
38424 {
38425 if (channels == 1 && channelMask == 0) {
38426 pChannelMap[0] = MA_CHANNEL_MONO;
38427 } else if (channels == 2 && channelMask == 0) {
38428 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
38429 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
38430 } else {
38431 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
38432 pChannelMap[0] = MA_CHANNEL_MONO;
38433 } else {
38434 /* Just iterate over each bit. */
38435 ma_uint32 iChannel = 0;
38436 ma_uint32 iBit;
38437 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
38438 SLuint32 bitValue = (channelMask & (1UL << iBit));
38439 if (bitValue != 0) {
38440 /* The bit is set. */
38441 pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
38442 iChannel += 1;
38443 }
38444 }
38445 }
38446 }
38447 }
38448
38449 static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
38450 {
38451 if (samplesPerSec <= SL_SAMPLINGRATE_8) {
38452 return SL_SAMPLINGRATE_8;
38453 }
38454 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
38455 return SL_SAMPLINGRATE_11_025;
38456 }
38457 if (samplesPerSec <= SL_SAMPLINGRATE_12) {
38458 return SL_SAMPLINGRATE_12;
38459 }
38460 if (samplesPerSec <= SL_SAMPLINGRATE_16) {
38461 return SL_SAMPLINGRATE_16;
38462 }
38463 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
38464 return SL_SAMPLINGRATE_22_05;
38465 }
38466 if (samplesPerSec <= SL_SAMPLINGRATE_24) {
38467 return SL_SAMPLINGRATE_24;
38468 }
38469 if (samplesPerSec <= SL_SAMPLINGRATE_32) {
38470 return SL_SAMPLINGRATE_32;
38471 }
38472 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
38473 return SL_SAMPLINGRATE_44_1;
38474 }
38475 if (samplesPerSec <= SL_SAMPLINGRATE_48) {
38476 return SL_SAMPLINGRATE_48;
38477 }
38478
38479 /* Android doesn't support more than 48000. */
38480 #ifndef MA_ANDROID
38481 if (samplesPerSec <= SL_SAMPLINGRATE_64) {
38482 return SL_SAMPLINGRATE_64;
38483 }
38484 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
38485 return SL_SAMPLINGRATE_88_2;
38486 }
38487 if (samplesPerSec <= SL_SAMPLINGRATE_96) {
38488 return SL_SAMPLINGRATE_96;
38489 }
38490 if (samplesPerSec <= SL_SAMPLINGRATE_192) {
38491 return SL_SAMPLINGRATE_192;
38492 }
38493 #endif
38494
38495 return SL_SAMPLINGRATE_16;
38496 }
38497
38498
38499 static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)
38500 {
38501 switch (streamType) {
38502 case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE;
38503 case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM;
38504 case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING;
38505 case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA;
38506 case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM;
38507 case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;
38508 default: break;
38509 }
38510
38511 return SL_ANDROID_STREAM_VOICE;
38512 }
38513
38514 static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)
38515 {
38516 switch (recordingPreset) {
38517 case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC;
38518 case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER;
38519 case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
38520 case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
38521 case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
38522 default: break;
38523 }
38524
38525 return SL_ANDROID_RECORDING_PRESET_NONE;
38526 }
38527
38528
38529 static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
38530 {
38531 ma_bool32 cbResult;
38532
38533 MA_ASSERT(pContext != NULL);
38534 MA_ASSERT(callback != NULL);
38535
38536 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. */
38537 if (g_maOpenSLInitCounter == 0) {
38538 return MA_INVALID_OPERATION;
38539 }
38540
38541 /*
38542 TODO: Test Me.
38543
38544 This is currently untested, so for now we are just returning default devices.
38545 */
38546 #if 0 && !defined(MA_ANDROID)
38547 ma_bool32 isTerminated = MA_FALSE;
38548
38549 SLuint32 pDeviceIDs[128];
38550 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
38551
38552 SLAudioIODeviceCapabilitiesItf deviceCaps;
38553 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
38554 if (resultSL != SL_RESULT_SUCCESS) {
38555 /* The interface may not be supported so just report a default device. */
38556 goto return_default_device;
38557 }
38558
38559 /* Playback */
38560 if (!isTerminated) {
38561 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
38562 if (resultSL != SL_RESULT_SUCCESS) {
38563 return ma_result_from_OpenSL(resultSL);
38564 }
38565
38566 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
38567 ma_device_info deviceInfo;
38568 MA_ZERO_OBJECT(&deviceInfo);
38569 deviceInfo.id.opensl = pDeviceIDs[iDevice];
38570
38571 SLAudioOutputDescriptor desc;
38572 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
38573 if (resultSL == SL_RESULT_SUCCESS) {
38574 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
38575
38576 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
38577 if (cbResult == MA_FALSE) {
38578 isTerminated = MA_TRUE;
38579 break;
38580 }
38581 }
38582 }
38583 }
38584
38585 /* Capture */
38586 if (!isTerminated) {
38587 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
38588 if (resultSL != SL_RESULT_SUCCESS) {
38589 return ma_result_from_OpenSL(resultSL);
38590 }
38591
38592 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
38593 ma_device_info deviceInfo;
38594 MA_ZERO_OBJECT(&deviceInfo);
38595 deviceInfo.id.opensl = pDeviceIDs[iDevice];
38596
38597 SLAudioInputDescriptor desc;
38598 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
38599 if (resultSL == SL_RESULT_SUCCESS) {
38600 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
38601
38602 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
38603 if (cbResult == MA_FALSE) {
38604 isTerminated = MA_TRUE;
38605 break;
38606 }
38607 }
38608 }
38609 }
38610
38611 return MA_SUCCESS;
38612 #else
38613 goto return_default_device;
38614 #endif
38615
38616 return_default_device:;
38617 cbResult = MA_TRUE;
38618
38619 /* Playback. */
38620 if (cbResult) {
38621 ma_device_info deviceInfo;
38622 MA_ZERO_OBJECT(&deviceInfo);
38623 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
38624 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38625 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
38626 }
38627
38628 /* Capture. */
38629 if (cbResult) {
38630 ma_device_info deviceInfo;
38631 MA_ZERO_OBJECT(&deviceInfo);
38632 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
38633 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38634 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
38635 }
38636
38637 return MA_SUCCESS;
38638 }
38639
38640 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)
38641 {
38642 MA_ASSERT(pContext != NULL);
38643 MA_ASSERT(pDeviceInfo != NULL);
38644
38645 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
38646 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
38647 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
38648 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
38649 pDeviceInfo->nativeDataFormatCount += 1;
38650 }
38651
38652 static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
38653 {
38654 ma_uint32 minChannels = 1;
38655 ma_uint32 maxChannels = 2;
38656 ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000;
38657 ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000;
38658 ma_uint32 iChannel;
38659 ma_uint32 iSampleRate;
38660
38661 MA_ASSERT(pContext != NULL);
38662 MA_ASSERT(pDeviceInfo != NULL);
38663
38664 /*
38665 Each sample format can support mono and stereo, and we'll support a small subset of standard
38666 rates (up to 48000). A better solution would be to somehow find a native sample rate.
38667 */
38668 for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
38669 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
38670 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
38671 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
38672 ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
38673 }
38674 }
38675 }
38676 }
38677
38678 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)
38679 {
38680 MA_ASSERT(pContext != NULL);
38681
38682 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. */
38683 if (g_maOpenSLInitCounter == 0) {
38684 return MA_INVALID_OPERATION;
38685 }
38686
38687 /*
38688 TODO: Test Me.
38689
38690 This is currently untested, so for now we are just returning default devices.
38691 */
38692 #if 0 && !defined(MA_ANDROID)
38693 SLAudioIODeviceCapabilitiesItf deviceCaps;
38694 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
38695 if (resultSL != SL_RESULT_SUCCESS) {
38696 /* The interface may not be supported so just report a default device. */
38697 goto return_default_device;
38698 }
38699
38700 if (deviceType == ma_device_type_playback) {
38701 SLAudioOutputDescriptor desc;
38702 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
38703 if (resultSL != SL_RESULT_SUCCESS) {
38704 return ma_result_from_OpenSL(resultSL);
38705 }
38706
38707 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
38708 } else {
38709 SLAudioInputDescriptor desc;
38710 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
38711 if (resultSL != SL_RESULT_SUCCESS) {
38712 return ma_result_from_OpenSL(resultSL);
38713 }
38714
38715 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
38716 }
38717
38718 goto return_detailed_info;
38719 #else
38720 goto return_default_device;
38721 #endif
38722
38723 return_default_device:
38724 if (pDeviceID != NULL) {
38725 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
38726 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
38727 return MA_NO_DEVICE; /* Don't know the device. */
38728 }
38729 }
38730
38731 /* ID and Name / Description */
38732 if (deviceType == ma_device_type_playback) {
38733 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
38734 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38735 } else {
38736 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
38737 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38738 }
38739
38740 pDeviceInfo->isDefault = MA_TRUE;
38741
38742 goto return_detailed_info;
38743
38744
38745 return_detailed_info:
38746
38747 /*
38748 For now we're just outputting a set of values that are supported by the API but not necessarily supported
38749 by the device natively. Later on we should work on this so that it more closely reflects the device's
38750 actual native format.
38751 */
38752 pDeviceInfo->nativeDataFormatCount = 0;
38753 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
38754 ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
38755 #endif
38756 ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
38757 ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
38758
38759 return MA_SUCCESS;
38760 }
38761
38762
38763 #ifdef MA_ANDROID
38764 /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
38765 static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
38766 {
38767 ma_device* pDevice = (ma_device*)pUserData;
38768 size_t periodSizeInBytes;
38769 ma_uint8* pBuffer;
38770 SLresult resultSL;
38771
38772 MA_ASSERT(pDevice != NULL);
38773
38774 (void)pBufferQueue;
38775
38776 /*
38777 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
38778 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
38779 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
38780 */
38781
38782 /* Don't do anything if the device is not started. */
38783 if (ma_device_get_state(pDevice) != ma_device_state_started) {
38784 return;
38785 }
38786
38787 /* Don't do anything if the device is being drained. */
38788 if (pDevice->opensl.isDrainingCapture) {
38789 return;
38790 }
38791
38792 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
38793 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
38794
38795 ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames);
38796
38797 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
38798 if (resultSL != SL_RESULT_SUCCESS) {
38799 return;
38800 }
38801
38802 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
38803 }
38804
38805 static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
38806 {
38807 ma_device* pDevice = (ma_device*)pUserData;
38808 size_t periodSizeInBytes;
38809 ma_uint8* pBuffer;
38810 SLresult resultSL;
38811
38812 MA_ASSERT(pDevice != NULL);
38813
38814 (void)pBufferQueue;
38815
38816 /* Don't do anything if the device is not started. */
38817 if (ma_device_get_state(pDevice) != ma_device_state_started) {
38818 return;
38819 }
38820
38821 /* Don't do anything if the device is being drained. */
38822 if (pDevice->opensl.isDrainingPlayback) {
38823 return;
38824 }
38825
38826 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
38827 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
38828
38829 ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames);
38830
38831 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
38832 if (resultSL != SL_RESULT_SUCCESS) {
38833 return;
38834 }
38835
38836 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
38837 }
38838 #endif
38839
38840 static ma_result ma_device_uninit__opensl(ma_device* pDevice)
38841 {
38842 MA_ASSERT(pDevice != NULL);
38843
38844 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. */
38845 if (g_maOpenSLInitCounter == 0) {
38846 return MA_INVALID_OPERATION;
38847 }
38848
38849 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38850 if (pDevice->opensl.pAudioRecorderObj) {
38851 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
38852 }
38853
38854 ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
38855 }
38856
38857 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38858 if (pDevice->opensl.pAudioPlayerObj) {
38859 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
38860 }
38861 if (pDevice->opensl.pOutputMixObj) {
38862 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
38863 }
38864
38865 ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
38866 }
38867
38868 return MA_SUCCESS;
38869 }
38870
38871 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
38872 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
38873 #else
38874 typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
38875 #endif
38876
38877 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)
38878 {
38879 /* We need to convert our format/channels/rate so that they aren't set to default. */
38880 if (format == ma_format_unknown) {
38881 format = MA_DEFAULT_FORMAT;
38882 }
38883 if (channels == 0) {
38884 channels = MA_DEFAULT_CHANNELS;
38885 }
38886 if (sampleRate == 0) {
38887 sampleRate = MA_DEFAULT_SAMPLE_RATE;
38888 }
38889
38890 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
38891 if (format == ma_format_f32) {
38892 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
38893 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
38894 } else {
38895 pDataFormat->formatType = SL_DATAFORMAT_PCM;
38896 }
38897 #else
38898 pDataFormat->formatType = SL_DATAFORMAT_PCM;
38899 #endif
38900
38901 pDataFormat->numChannels = channels;
38902 ((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 */
38903 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8;
38904 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
38905 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
38906
38907 /*
38908 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
38909 - Only mono and stereo is supported.
38910 - Only u8 and s16 formats are supported.
38911 - Maximum sample rate of 48000.
38912 */
38913 #ifdef MA_ANDROID
38914 if (pDataFormat->numChannels > 2) {
38915 pDataFormat->numChannels = 2;
38916 }
38917 #if __ANDROID_API__ >= 21
38918 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
38919 /* It's floating point. */
38920 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
38921 if (pDataFormat->bitsPerSample > 32) {
38922 pDataFormat->bitsPerSample = 32;
38923 }
38924 } else {
38925 if (pDataFormat->bitsPerSample > 16) {
38926 pDataFormat->bitsPerSample = 16;
38927 }
38928 }
38929 #else
38930 if (pDataFormat->bitsPerSample > 16) {
38931 pDataFormat->bitsPerSample = 16;
38932 }
38933 #endif
38934 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
38935 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
38936 }
38937 #endif
38938
38939 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
38940
38941 return MA_SUCCESS;
38942 }
38943
38944 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)
38945 {
38946 ma_bool32 isFloatingPoint = MA_FALSE;
38947 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
38948 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
38949 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
38950 isFloatingPoint = MA_TRUE;
38951 }
38952 #endif
38953 if (isFloatingPoint) {
38954 if (pDataFormat->bitsPerSample == 32) {
38955 *pFormat = ma_format_f32;
38956 }
38957 } else {
38958 if (pDataFormat->bitsPerSample == 8) {
38959 *pFormat = ma_format_u8;
38960 } else if (pDataFormat->bitsPerSample == 16) {
38961 *pFormat = ma_format_s16;
38962 } else if (pDataFormat->bitsPerSample == 24) {
38963 *pFormat = ma_format_s24;
38964 } else if (pDataFormat->bitsPerSample == 32) {
38965 *pFormat = ma_format_s32;
38966 }
38967 }
38968
38969 *pChannels = pDataFormat->numChannels;
38970 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
38971 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
38972
38973 return MA_SUCCESS;
38974 }
38975
38976 static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
38977 {
38978 #ifdef MA_ANDROID
38979 SLDataLocator_AndroidSimpleBufferQueue queue;
38980 SLresult resultSL;
38981 size_t bufferSizeInBytes;
38982 SLInterfaceID itfIDs[2];
38983 const SLboolean itfIDsRequired[] = {
38984 SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */
38985 SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */
38986 };
38987 #endif
38988
38989 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. */
38990 if (g_maOpenSLInitCounter == 0) {
38991 return MA_INVALID_OPERATION;
38992 }
38993
38994 if (pConfig->deviceType == ma_device_type_loopback) {
38995 return MA_DEVICE_TYPE_NOT_SUPPORTED;
38996 }
38997
38998 /*
38999 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
39000 been able to test with and I currently depend on Android-specific extensions (simple buffer
39001 queues).
39002 */
39003 #ifdef MA_ANDROID
39004 itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
39005 itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;
39006
39007 /* No exclusive mode with OpenSL|ES. */
39008 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
39009 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
39010 return MA_SHARE_MODE_NOT_SUPPORTED;
39011 }
39012
39013 /* Now we can start initializing the device properly. */
39014 MA_ASSERT(pDevice != NULL);
39015 MA_ZERO_OBJECT(&pDevice->opensl);
39016
39017 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
39018
39019 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
39020 ma_SLDataFormat_PCM pcm;
39021 SLDataLocator_IODevice locatorDevice;
39022 SLDataSource source;
39023 SLDataSink sink;
39024 SLAndroidConfigurationItf pRecorderConfig;
39025
39026 ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
39027
39028 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
39029 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
39030 locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */
39031 locatorDevice.device = NULL;
39032
39033 source.pLocator = &locatorDevice;
39034 source.pFormat = NULL;
39035
39036 queue.numBuffers = pDescriptorCapture->periodCount;
39037
39038 sink.pLocator = &queue;
39039 sink.pFormat = (SLDataFormat_PCM*)&pcm;
39040
39041 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39042 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
39043 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
39044 pcm.formatType = SL_DATAFORMAT_PCM;
39045 pcm.numChannels = 1;
39046 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
39047 pcm.bitsPerSample = 16;
39048 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
39049 pcm.channelMask = 0;
39050 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39051 }
39052
39053 if (resultSL != SL_RESULT_SUCCESS) {
39054 ma_device_uninit__opensl(pDevice);
39055 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.");
39056 return ma_result_from_OpenSL(resultSL);
39057 }
39058
39059
39060 /* Set the recording preset before realizing the player. */
39061 if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) {
39062 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
39063 if (resultSL == SL_RESULT_SUCCESS) {
39064 SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
39065 resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
39066 if (resultSL != SL_RESULT_SUCCESS) {
39067 /* Failed to set the configuration. Just keep going. */
39068 }
39069 }
39070 }
39071
39072 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
39073 if (resultSL != SL_RESULT_SUCCESS) {
39074 ma_device_uninit__opensl(pDevice);
39075 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.");
39076 return ma_result_from_OpenSL(resultSL);
39077 }
39078
39079 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
39080 if (resultSL != SL_RESULT_SUCCESS) {
39081 ma_device_uninit__opensl(pDevice);
39082 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.");
39083 return ma_result_from_OpenSL(resultSL);
39084 }
39085
39086 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
39087 if (resultSL != SL_RESULT_SUCCESS) {
39088 ma_device_uninit__opensl(pDevice);
39089 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
39090 return ma_result_from_OpenSL(resultSL);
39091 }
39092
39093 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
39094 if (resultSL != SL_RESULT_SUCCESS) {
39095 ma_device_uninit__opensl(pDevice);
39096 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
39097 return ma_result_from_OpenSL(resultSL);
39098 }
39099
39100 /* The internal format is determined by the "pcm" object. */
39101 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
39102
39103 /* Buffer. */
39104 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
39105 pDevice->opensl.currentBufferIndexCapture = 0;
39106
39107 bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
39108 pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
39109 if (pDevice->opensl.pBufferCapture == NULL) {
39110 ma_device_uninit__opensl(pDevice);
39111 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
39112 return MA_OUT_OF_MEMORY;
39113 }
39114 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
39115 }
39116
39117 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
39118 ma_SLDataFormat_PCM pcm;
39119 SLDataSource source;
39120 SLDataLocator_OutputMix outmixLocator;
39121 SLDataSink sink;
39122 SLAndroidConfigurationItf pPlayerConfig;
39123
39124 ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
39125
39126 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
39127 if (resultSL != SL_RESULT_SUCCESS) {
39128 ma_device_uninit__opensl(pDevice);
39129 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.");
39130 return ma_result_from_OpenSL(resultSL);
39131 }
39132
39133 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
39134 if (resultSL != SL_RESULT_SUCCESS) {
39135 ma_device_uninit__opensl(pDevice);
39136 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.");
39137 return ma_result_from_OpenSL(resultSL);
39138 }
39139
39140 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
39141 if (resultSL != SL_RESULT_SUCCESS) {
39142 ma_device_uninit__opensl(pDevice);
39143 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.");
39144 return ma_result_from_OpenSL(resultSL);
39145 }
39146
39147 /* Set the output device. */
39148 if (pDescriptorPlayback->pDeviceID != NULL) {
39149 SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
39150 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
39151 }
39152
39153 queue.numBuffers = pDescriptorPlayback->periodCount;
39154
39155 source.pLocator = &queue;
39156 source.pFormat = (SLDataFormat_PCM*)&pcm;
39157
39158 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
39159 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
39160
39161 sink.pLocator = &outmixLocator;
39162 sink.pFormat = NULL;
39163
39164 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39165 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
39166 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
39167 pcm.formatType = SL_DATAFORMAT_PCM;
39168 pcm.numChannels = 2;
39169 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
39170 pcm.bitsPerSample = 16;
39171 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
39172 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
39173 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
39174 }
39175
39176 if (resultSL != SL_RESULT_SUCCESS) {
39177 ma_device_uninit__opensl(pDevice);
39178 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.");
39179 return ma_result_from_OpenSL(resultSL);
39180 }
39181
39182
39183 /* Set the stream type before realizing the player. */
39184 if (pConfig->opensl.streamType != ma_opensl_stream_type_default) {
39185 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
39186 if (resultSL == SL_RESULT_SUCCESS) {
39187 SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
39188 resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
39189 if (resultSL != SL_RESULT_SUCCESS) {
39190 /* Failed to set the configuration. Just keep going. */
39191 }
39192 }
39193 }
39194
39195 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
39196 if (resultSL != SL_RESULT_SUCCESS) {
39197 ma_device_uninit__opensl(pDevice);
39198 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.");
39199 return ma_result_from_OpenSL(resultSL);
39200 }
39201
39202 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
39203 if (resultSL != SL_RESULT_SUCCESS) {
39204 ma_device_uninit__opensl(pDevice);
39205 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.");
39206 return ma_result_from_OpenSL(resultSL);
39207 }
39208
39209 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
39210 if (resultSL != SL_RESULT_SUCCESS) {
39211 ma_device_uninit__opensl(pDevice);
39212 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
39213 return ma_result_from_OpenSL(resultSL);
39214 }
39215
39216 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
39217 if (resultSL != SL_RESULT_SUCCESS) {
39218 ma_device_uninit__opensl(pDevice);
39219 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
39220 return ma_result_from_OpenSL(resultSL);
39221 }
39222
39223 /* The internal format is determined by the "pcm" object. */
39224 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));
39225
39226 /* Buffer. */
39227 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
39228 pDevice->opensl.currentBufferIndexPlayback = 0;
39229
39230 bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;
39231 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
39232 if (pDevice->opensl.pBufferPlayback == NULL) {
39233 ma_device_uninit__opensl(pDevice);
39234 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
39235 return MA_OUT_OF_MEMORY;
39236 }
39237 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
39238 }
39239
39240 return MA_SUCCESS;
39241 #else
39242 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
39243 #endif
39244 }
39245
39246 static ma_result ma_device_start__opensl(ma_device* pDevice)
39247 {
39248 SLresult resultSL;
39249 size_t periodSizeInBytes;
39250 ma_uint32 iPeriod;
39251
39252 MA_ASSERT(pDevice != NULL);
39253
39254 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. */
39255 if (g_maOpenSLInitCounter == 0) {
39256 return MA_INVALID_OPERATION;
39257 }
39258
39259 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39260 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
39261 if (resultSL != SL_RESULT_SUCCESS) {
39262 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.");
39263 return ma_result_from_OpenSL(resultSL);
39264 }
39265
39266 periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
39267 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
39268 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
39269 if (resultSL != SL_RESULT_SUCCESS) {
39270 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
39271 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.");
39272 return ma_result_from_OpenSL(resultSL);
39273 }
39274 }
39275 }
39276
39277 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39278 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
39279 if (resultSL != SL_RESULT_SUCCESS) {
39280 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.");
39281 return ma_result_from_OpenSL(resultSL);
39282 }
39283
39284 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
39285 if (pDevice->type == ma_device_type_duplex) {
39286 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
39287 } else {
39288 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
39289 }
39290
39291 periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
39292 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
39293 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
39294 if (resultSL != SL_RESULT_SUCCESS) {
39295 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
39296 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.");
39297 return ma_result_from_OpenSL(resultSL);
39298 }
39299 }
39300 }
39301
39302 return MA_SUCCESS;
39303 }
39304
39305 static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
39306 {
39307 SLAndroidSimpleBufferQueueItf pBufferQueue;
39308
39309 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
39310
39311 if (pDevice->type == ma_device_type_capture) {
39312 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
39313 pDevice->opensl.isDrainingCapture = MA_TRUE;
39314 } else {
39315 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
39316 pDevice->opensl.isDrainingPlayback = MA_TRUE;
39317 }
39318
39319 for (;;) {
39320 SLAndroidSimpleBufferQueueState state;
39321
39322 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
39323 if (state.count == 0) {
39324 break;
39325 }
39326
39327 ma_sleep(10);
39328 }
39329
39330 if (pDevice->type == ma_device_type_capture) {
39331 pDevice->opensl.isDrainingCapture = MA_FALSE;
39332 } else {
39333 pDevice->opensl.isDrainingPlayback = MA_FALSE;
39334 }
39335
39336 return MA_SUCCESS;
39337 }
39338
39339 static ma_result ma_device_stop__opensl(ma_device* pDevice)
39340 {
39341 SLresult resultSL;
39342
39343 MA_ASSERT(pDevice != NULL);
39344
39345 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. */
39346 if (g_maOpenSLInitCounter == 0) {
39347 return MA_INVALID_OPERATION;
39348 }
39349
39350 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39351 ma_device_drain__opensl(pDevice, ma_device_type_capture);
39352
39353 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
39354 if (resultSL != SL_RESULT_SUCCESS) {
39355 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.");
39356 return ma_result_from_OpenSL(resultSL);
39357 }
39358
39359 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
39360 }
39361
39362 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39363 ma_device_drain__opensl(pDevice, ma_device_type_playback);
39364
39365 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
39366 if (resultSL != SL_RESULT_SUCCESS) {
39367 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.");
39368 return ma_result_from_OpenSL(resultSL);
39369 }
39370
39371 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
39372 }
39373
39374 /* 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. */
39375 ma_device__on_notification_stopped(pDevice);
39376
39377 return MA_SUCCESS;
39378 }
39379
39380
39381 static ma_result ma_context_uninit__opensl(ma_context* pContext)
39382 {
39383 MA_ASSERT(pContext != NULL);
39384 MA_ASSERT(pContext->backend == ma_backend_opensl);
39385 (void)pContext;
39386
39387 /* Uninit global data. */
39388 ma_spinlock_lock(&g_maOpenSLSpinlock);
39389 {
39390 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(). */
39391
39392 g_maOpenSLInitCounter -= 1;
39393 if (g_maOpenSLInitCounter == 0) {
39394 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
39395 }
39396 }
39397 ma_spinlock_unlock(&g_maOpenSLSpinlock);
39398
39399 return MA_SUCCESS;
39400 }
39401
39402 static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
39403 {
39404 /* 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. */
39405 ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName);
39406 if (p == NULL) {
39407 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName);
39408 return MA_NO_BACKEND;
39409 }
39410
39411 *pHandle = *p;
39412 return MA_SUCCESS;
39413 }
39414
39415 static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
39416 {
39417 g_maOpenSLInitCounter += 1;
39418 if (g_maOpenSLInitCounter == 1) {
39419 SLresult resultSL;
39420
39421 resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
39422 if (resultSL != SL_RESULT_SUCCESS) {
39423 g_maOpenSLInitCounter -= 1;
39424 return ma_result_from_OpenSL(resultSL);
39425 }
39426
39427 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
39428
39429 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
39430 if (resultSL != SL_RESULT_SUCCESS) {
39431 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
39432 g_maOpenSLInitCounter -= 1;
39433 return ma_result_from_OpenSL(resultSL);
39434 }
39435 }
39436
39437 return MA_SUCCESS;
39438 }
39439
39440 static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
39441 {
39442 ma_result result;
39443
39444 #if !defined(MA_NO_RUNTIME_LINKING)
39445 size_t i;
39446 const char* libOpenSLESNames[] = {
39447 "libOpenSLES.so"
39448 };
39449 #endif
39450
39451 MA_ASSERT(pContext != NULL);
39452
39453 (void)pConfig;
39454
39455 #if !defined(MA_NO_RUNTIME_LINKING)
39456 /*
39457 Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
39458 report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
39459 and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
39460 references to the symbols and will hopefully skip the checks.
39461 */
39462 for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
39463 pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]);
39464 if (pContext->opensl.libOpenSLES != NULL) {
39465 break;
39466 }
39467 }
39468
39469 if (pContext->opensl.libOpenSLES == NULL) {
39470 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so");
39471 return MA_NO_BACKEND;
39472 }
39473
39474 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
39475 if (result != MA_SUCCESS) {
39476 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39477 return result;
39478 }
39479
39480 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
39481 if (result != MA_SUCCESS) {
39482 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39483 return result;
39484 }
39485
39486 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
39487 if (result != MA_SUCCESS) {
39488 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39489 return result;
39490 }
39491
39492 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
39493 if (result != MA_SUCCESS) {
39494 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39495 return result;
39496 }
39497
39498 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
39499 if (result != MA_SUCCESS) {
39500 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39501 return result;
39502 }
39503
39504 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
39505 if (result != MA_SUCCESS) {
39506 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39507 return result;
39508 }
39509
39510 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
39511 if (result != MA_SUCCESS) {
39512 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39513 return result;
39514 }
39515
39516 pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine");
39517 if (pContext->opensl.slCreateEngine == NULL) {
39518 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39519 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine.");
39520 return MA_NO_BACKEND;
39521 }
39522 #else
39523 pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
39524 pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
39525 pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
39526 pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
39527 pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
39528 pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
39529 pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
39530 pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
39531 #endif
39532
39533
39534 /* Initialize global data first if applicable. */
39535 ma_spinlock_lock(&g_maOpenSLSpinlock);
39536 {
39537 result = ma_context_init_engine_nolock__opensl(pContext);
39538 }
39539 ma_spinlock_unlock(&g_maOpenSLSpinlock);
39540
39541 if (result != MA_SUCCESS) {
39542 ma_dlclose(pContext, pContext->opensl.libOpenSLES);
39543 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine.");
39544 return result;
39545 }
39546
39547 pCallbacks->onContextInit = ma_context_init__opensl;
39548 pCallbacks->onContextUninit = ma_context_uninit__opensl;
39549 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
39550 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
39551 pCallbacks->onDeviceInit = ma_device_init__opensl;
39552 pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
39553 pCallbacks->onDeviceStart = ma_device_start__opensl;
39554 pCallbacks->onDeviceStop = ma_device_stop__opensl;
39555 pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */
39556 pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */
39557 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */
39558
39559 return MA_SUCCESS;
39560 }
39561 #endif /* OpenSL|ES */
39562
39563
39564 /******************************************************************************
39565
39566 Web Audio Backend
39567
39568 ******************************************************************************/
39569 #ifdef MA_HAS_WEBAUDIO
39570 #include <emscripten/emscripten.h>
39571
39572 #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32)))
39573 #include <emscripten/webaudio.h>
39574 #define MA_SUPPORT_AUDIO_WORKLETS
39575 #endif
39576
39577 /*
39578 TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS.
39579 */
39580 #if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS)
39581 #define MA_USE_AUDIO_WORKLETS
39582 #endif
39583
39584 /* The thread stack size must be a multiple of 16. */
39585 #ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE
39586 #define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384
39587 #endif
39588
39589 #if defined(MA_USE_AUDIO_WORKLETS)
39590 #define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced"
39591 #define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive"
39592 #define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback"
39593 #endif
39594
39595 static ma_bool32 ma_is_capture_supported__webaudio()
39596 {
39597 return EM_ASM_INT({
39598 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
39599 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
39600 }
39601
39602 #ifdef __cplusplus
39603 extern "C" {
39604 #endif
39605 void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
39606 {
39607 return ma_malloc(sz, pAllocationCallbacks);
39608 }
39609
39610 void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
39611 {
39612 ma_free(p, pAllocationCallbacks);
39613 }
39614
39615 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
39616 {
39617 ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
39618 }
39619
39620 void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
39621 {
39622 ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);
39623 }
39624 #ifdef __cplusplus
39625 }
39626 #endif
39627
39628 static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
39629 {
39630 ma_bool32 cbResult = MA_TRUE;
39631
39632 MA_ASSERT(pContext != NULL);
39633 MA_ASSERT(callback != NULL);
39634
39635 /* Only supporting default devices for now. */
39636
39637 /* Playback. */
39638 if (cbResult) {
39639 ma_device_info deviceInfo;
39640 MA_ZERO_OBJECT(&deviceInfo);
39641 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
39642 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
39643 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
39644 }
39645
39646 /* Capture. */
39647 if (cbResult) {
39648 if (ma_is_capture_supported__webaudio()) {
39649 ma_device_info deviceInfo;
39650 MA_ZERO_OBJECT(&deviceInfo);
39651 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
39652 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
39653 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
39654 }
39655 }
39656
39657 return MA_SUCCESS;
39658 }
39659
39660 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)
39661 {
39662 MA_ASSERT(pContext != NULL);
39663
39664 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
39665 return MA_NO_DEVICE;
39666 }
39667
39668 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
39669
39670 /* Only supporting default devices for now. */
39671 (void)pDeviceID;
39672 if (deviceType == ma_device_type_playback) {
39673 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
39674 } else {
39675 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
39676 }
39677
39678 /* Only supporting default devices. */
39679 pDeviceInfo->isDefault = MA_TRUE;
39680
39681 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
39682 pDeviceInfo->nativeDataFormats[0].flags = 0;
39683 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
39684 pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */
39685 pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({
39686 try {
39687 var temp = new (window.AudioContext || window.webkitAudioContext)();
39688 var sampleRate = temp.sampleRate;
39689 temp.close();
39690 return sampleRate;
39691 } catch(e) {
39692 return 0;
39693 }
39694 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
39695
39696 if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {
39697 return MA_NO_DEVICE;
39698 }
39699
39700 pDeviceInfo->nativeDataFormatCount = 1;
39701
39702 return MA_SUCCESS;
39703 }
39704
39705 #if !defined(MA_USE_AUDIO_WORKLETS)
39706 static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
39707 {
39708 MA_ASSERT(pDevice != NULL);
39709
39710 EM_ASM({
39711 var device = miniaudio.get_device_by_index($0);
39712 var pAllocationCallbacks = $3;
39713
39714 /* Make sure all nodes are disconnected and marked for collection. */
39715 if (device.scriptNode !== undefined) {
39716 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... */
39717 device.scriptNode.disconnect();
39718 device.scriptNode = undefined;
39719 }
39720 if (device.streamNode !== undefined) {
39721 device.streamNode.disconnect();
39722 device.streamNode = undefined;
39723 }
39724
39725 /*
39726 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
39727 to clear the callback before closing.
39728 */
39729 device.webaudio.close();
39730 device.webaudio = undefined;
39731
39732 /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
39733 if (device.intermediaryBuffer !== undefined) {
39734 _ma_free_emscripten(device.intermediaryBuffer, pAllocationCallbacks);
39735 device.intermediaryBuffer = undefined;
39736 device.intermediaryBufferView = undefined;
39737 device.intermediaryBufferSizeInBytes = undefined;
39738 }
39739
39740 /* Make sure the device is untracked so the slot can be reused later. */
39741 miniaudio.untrack_device_by_index($0);
39742 }, deviceIndex, deviceType, &pDevice->pContext->allocationCallbacks);
39743 }
39744 #endif
39745
39746 static void ma_device_uninit_by_type__webaudio(ma_device* pDevice, ma_device_type deviceType)
39747 {
39748 MA_ASSERT(pDevice != NULL);
39749 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
39750
39751 #if defined(MA_USE_AUDIO_WORKLETS)
39752 if (deviceType == ma_device_type_capture) {
39753 ma_free(pDevice->webaudio.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
39754 ma_free(pDevice->webaudio.pStackBufferCapture, &pDevice->pContext->allocationCallbacks);
39755 emscripten_destroy_audio_context(pDevice->webaudio.audioContextCapture);
39756 } else {
39757 ma_free(pDevice->webaudio.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
39758 ma_free(pDevice->webaudio.pStackBufferPlayback, &pDevice->pContext->allocationCallbacks);
39759 emscripten_destroy_audio_context(pDevice->webaudio.audioContextPlayback);
39760 }
39761 #else
39762 if (deviceType == ma_device_type_capture) {
39763 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
39764 } else {
39765 ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
39766 }
39767 #endif
39768 }
39769
39770 static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
39771 {
39772 MA_ASSERT(pDevice != NULL);
39773
39774 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39775 ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture);
39776 }
39777
39778 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39779 ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_playback);
39780 }
39781
39782 return MA_SUCCESS;
39783 }
39784
39785 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
39786 {
39787 #if defined(MA_USE_AUDIO_WORKLETS)
39788 (void)pDescriptor;
39789 (void)nativeSampleRate;
39790 (void)performanceProfile;
39791
39792 return 256;
39793 #else
39794 /*
39795 There have been reports of the default buffer size being too small on some browsers. If we're using
39796 the default buffer size, we'll make sure the period size is bigger than our standard defaults.
39797 */
39798 ma_uint32 periodSizeInFrames;
39799
39800 if (pDescriptor->periodSizeInFrames == 0) {
39801 if (pDescriptor->periodSizeInMilliseconds == 0) {
39802 if (performanceProfile == ma_performance_profile_low_latency) {
39803 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */
39804 } else {
39805 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);
39806 }
39807 } else {
39808 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
39809 }
39810 } else {
39811 periodSizeInFrames = pDescriptor->periodSizeInFrames;
39812 }
39813
39814 /* The size of the buffer must be a power of 2 and between 256 and 16384. */
39815 if (periodSizeInFrames < 256) {
39816 periodSizeInFrames = 256;
39817 } else if (periodSizeInFrames > 16384) {
39818 periodSizeInFrames = 16384;
39819 } else {
39820 periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);
39821 }
39822
39823 return periodSizeInFrames;
39824 #endif
39825 }
39826
39827
39828 #if defined(MA_USE_AUDIO_WORKLETS)
39829 typedef struct
39830 {
39831 ma_device* pDevice;
39832 const ma_device_config* pConfig;
39833 ma_device_descriptor* pDescriptor;
39834 ma_device_type deviceType;
39835 ma_uint32 channels;
39836 } ma_audio_worklet_thread_initialized_data;
39837
39838 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)
39839 {
39840 ma_device* pDevice = (ma_device*)pUserData;
39841 ma_uint32 frameCount;
39842 ma_uint32 framesProcessed;
39843
39844 (void)paramCount;
39845 (void)pParams;
39846
39847 /*
39848 The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels
39849 like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer
39850 to variables instead of a hard coded number. In any case, will follow along for the time being.
39851
39852 Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio
39853 for further processing.
39854 */
39855 frameCount = 128;
39856
39857 /* Run the conversion logic in a loop for robustness. */
39858 framesProcessed = 0;
39859 while (framesProcessed < frameCount) {
39860 ma_uint32 framesToProcessThisIteration = frameCount - framesProcessed;
39861
39862 if (inputCount > 0) {
39863 if (framesToProcessThisIteration > pDevice->webaudio.intermediaryBufferSizeInFramesPlayback) {
39864 framesToProcessThisIteration = pDevice->webaudio.intermediaryBufferSizeInFramesPlayback;
39865 }
39866
39867 /* Input data needs to be interleaved before we hand it to the client. */
39868 for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) {
39869 for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) {
39870 pDevice->webaudio.pIntermediaryBufferCapture[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + framesProcessed + iFrame];
39871 }
39872 }
39873
39874 ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferCapture);
39875 }
39876
39877 if (outputCount > 0) {
39878 ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferPlayback);
39879
39880 /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */
39881 for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) {
39882 for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) {
39883 pOutputs[0].data[frameCount*iChannel + framesProcessed + iFrame] = pDevice->webaudio.pIntermediaryBufferPlayback[iFrame*pDevice->playback.internalChannels + iChannel];
39884 }
39885 }
39886 }
39887
39888 framesProcessed += framesToProcessThisIteration;
39889 }
39890
39891 return EM_TRUE;
39892 }
39893
39894
39895 static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
39896 {
39897 ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
39898 EmscriptenAudioWorkletNodeCreateOptions workletNodeOptions;
39899 EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletNode;
39900 int outputChannelCount = 0;
39901
39902 if (success == EM_FALSE) {
39903 pParameters->pDevice->webaudio.isInitialized = MA_TRUE;
39904 return;
39905 }
39906
39907 MA_ZERO_OBJECT(&workletNodeOptions);
39908
39909 if (pParameters->deviceType == ma_device_type_capture) {
39910 workletNodeOptions.numberOfInputs = 1;
39911 } else {
39912 outputChannelCount = (int)pParameters->channels; /* Safe cast. */
39913
39914 workletNodeOptions.numberOfOutputs = 1;
39915 workletNodeOptions.outputChannelCounts = &outputChannelCount;
39916 }
39917
39918 /* Here is where we create the node that will do our processing. */
39919 workletNode = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &workletNodeOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);
39920
39921 if (pParameters->deviceType == ma_device_type_capture) {
39922 pParameters->pDevice->webaudio.workletNodeCapture = workletNode;
39923 } else {
39924 pParameters->pDevice->webaudio.workletNodePlayback = workletNode;
39925 }
39926
39927 /*
39928 With the worklet node created we can now attach it to the graph. This is done differently depending on whether or not
39929 it's capture or playback mode.
39930 */
39931 if (pParameters->deviceType == ma_device_type_capture) {
39932 EM_ASM({
39933 var workletNode = emscriptenGetAudioObject($0);
39934 var audioContext = emscriptenGetAudioObject($1);
39935
39936 navigator.mediaDevices.getUserMedia({audio:true, video:false})
39937 .then(function(stream) {
39938 audioContext.streamNode = audioContext.createMediaStreamSource(stream);
39939 audioContext.streamNode.connect(workletNode);
39940
39941 /*
39942 Now that the worklet node has been connected, do we need to inspect workletNode.channelCount
39943 to check the actual channel count, or is it safe to assume it's always 2?
39944 */
39945 })
39946 .catch(function(error) {
39947
39948 });
39949 }, workletNode, audioContext);
39950 } else {
39951 EM_ASM({
39952 var workletNode = emscriptenGetAudioObject($0);
39953 var audioContext = emscriptenGetAudioObject($1);
39954 workletNode.connect(audioContext.destination);
39955 }, workletNode, audioContext);
39956 }
39957
39958 pParameters->pDevice->webaudio.isInitialized = MA_TRUE;
39959
39960 ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", workletNode);
39961
39962 /* Our parameter data is no longer needed. */
39963 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
39964 }
39965
39966
39967
39968 static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
39969 {
39970 ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
39971 WebAudioWorkletProcessorCreateOptions workletProcessorOptions;
39972
39973 MA_ASSERT(pParameters != NULL);
39974
39975 if (success == EM_FALSE) {
39976 pParameters->pDevice->webaudio.isInitialized = MA_TRUE;
39977 return;
39978 }
39979
39980 MA_ZERO_OBJECT(&workletProcessorOptions);
39981 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? */
39982
39983 emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters);
39984 }
39985 #endif
39986
39987 static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
39988 {
39989 #if defined(MA_USE_AUDIO_WORKLETS)
39990 EMSCRIPTEN_WEBAUDIO_T audioContext;
39991 void* pStackBuffer;
39992 size_t intermediaryBufferSizeInFrames;
39993 float* pIntermediaryBuffer;
39994 #endif
39995 ma_uint32 channels;
39996 ma_uint32 sampleRate;
39997 ma_uint32 periodSizeInFrames;
39998
39999 MA_ASSERT(pDevice != NULL);
40000 MA_ASSERT(pConfig != NULL);
40001 MA_ASSERT(deviceType != ma_device_type_duplex);
40002
40003 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
40004 return MA_NO_DEVICE;
40005 }
40006
40007 /* We're going to calculate some stuff in C just to simplify the JS code. */
40008 channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
40009 sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
40010 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile);
40011
40012 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames);
40013
40014 #if defined(MA_USE_AUDIO_WORKLETS)
40015 {
40016 ma_audio_worklet_thread_initialized_data* pInitParameters;
40017 EmscriptenWebAudioCreateAttributes audioContextAttributes;
40018
40019 audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE;
40020 audioContextAttributes.sampleRate = sampleRate;
40021
40022 /* 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. */
40023 audioContext = emscripten_create_audio_context(&audioContextAttributes);
40024
40025 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: AUDIO CONTEXT CREATED\n");
40026
40027 /*
40028 We now need to create a worker thread. This is a bit weird because we need to allocate our
40029 own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to
40030 allocate this on the heap to keep it simple.
40031 */
40032 pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks);
40033 if (pStackBuffer == NULL) {
40034 emscripten_destroy_audio_context(audioContext);
40035 return MA_OUT_OF_MEMORY;
40036 }
40037
40038 /*
40039 We need an intermediary buffer for data conversion. WebAudio reports data in uninterleaved
40040 format whereas we require it to be interleaved. We'll do this in chunks of 128 frames.
40041 */
40042 intermediaryBufferSizeInFrames = 128;
40043 pIntermediaryBuffer = ma_malloc(intermediaryBufferSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks);
40044 if (pIntermediaryBuffer == NULL) {
40045 ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
40046 emscripten_destroy_audio_context(audioContext);
40047 return MA_OUT_OF_MEMORY;
40048 }
40049
40050 pInitParameters = ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks);
40051 if (pInitParameters == NULL) {
40052 ma_free(pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
40053 ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
40054 emscripten_destroy_audio_context(audioContext);
40055 return MA_OUT_OF_MEMORY;
40056 }
40057
40058 pInitParameters->pDevice = pDevice;
40059 pInitParameters->pConfig = pConfig;
40060 pInitParameters->pDescriptor = pDescriptor;
40061 pInitParameters->deviceType = deviceType;
40062 pInitParameters->channels = channels;
40063
40064 /*
40065 We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of
40066 the Emscripten WebAudio stuff is asynchronous.
40067 */
40068 pDevice->webaudio.isInitialized = MA_FALSE;
40069
40070 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: CREATING WORKLET\n");
40071
40072 emscripten_start_wasm_audio_worklet_thread_async(audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters);
40073
40074 /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */
40075 while (pDevice->webaudio.isInitialized == MA_FALSE) {
40076 emscripten_sleep(1);
40077 }
40078
40079 /*
40080 Now that initialization is finished we can go ahead and extract our channel count so that
40081 miniaudio can set up a data converter at a higher level.
40082 */
40083 if (deviceType == ma_device_type_capture) {
40084 /*
40085 For capture we won't actually know what the channel count is. Everything I've seen seems
40086 to indicate that the default channel count is 2, so I'm sticking with that.
40087 */
40088 channels = 2;
40089 } else {
40090 /* Get the channel count from the audio context. */
40091 channels = (ma_uint32)EM_ASM_INT({
40092 return emscriptenGetAudioObject($0).destination.channelCount;
40093 }, audioContext);
40094 }
40095
40096 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: INITIALIZED. channels = %u\n", channels);
40097 }
40098 #else
40099 /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */
40100 int deviceIndex = EM_ASM_INT({
40101 var channels = $0;
40102 var sampleRate = $1;
40103 var bufferSize = $2; /* In PCM frames. */
40104 var isCapture = $3;
40105 var pDevice = $4;
40106 var pAllocationCallbacks = $5;
40107
40108 if (typeof(window.miniaudio) === 'undefined') {
40109 return -1; /* Context not initialized. */
40110 }
40111
40112 var device = {};
40113
40114 /* The AudioContext must be created in a suspended state. */
40115 device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
40116 device.webaudio.suspend();
40117 device.state = 1; /* ma_device_state_stopped */
40118
40119 /* We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. */
40120 device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
40121 device.intermediaryBuffer = _ma_malloc_emscripten(device.intermediaryBufferSizeInBytes, pAllocationCallbacks);
40122 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
40123
40124 /*
40125 Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
40126
40127 ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback
40128 that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
40129 something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
40130 work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL
40131 implementation. I'll be avoiding that insane AudioWorklet API like the plague...
40132
40133 For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the
40134 playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the
40135 MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've
40136 been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know
40137 how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like
40138 this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know!
40139 */
40140 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels);
40141
40142 if (isCapture) {
40143 device.scriptNode.onaudioprocess = function(e) {
40144 if (device.intermediaryBuffer === undefined) {
40145 return; /* This means the device has been uninitialized. */
40146 }
40147
40148 if (device.intermediaryBufferView.length == 0) {
40149 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
40150 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
40151 }
40152
40153 /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
40154 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
40155 e.outputBuffer.getChannelData(iChannel).fill(0.0);
40156 }
40157
40158 /* There are some situations where we may want to send silence to the client. */
40159 var sendSilence = false;
40160 if (device.streamNode === undefined) {
40161 sendSilence = true;
40162 }
40163
40164 /* Sanity check. This will never happen, right? */
40165 if (e.inputBuffer.numberOfChannels != channels) {
40166 console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
40167 sendSilence = true;
40168 }
40169
40170 /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
40171 var totalFramesProcessed = 0;
40172 while (totalFramesProcessed < e.inputBuffer.length) {
40173 var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
40174 var framesToProcess = framesRemaining;
40175 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
40176 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
40177 }
40178
40179 /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */
40180 if (sendSilence) {
40181 device.intermediaryBufferView.fill(0.0);
40182 } else {
40183 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
40184 for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
40185 device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
40186 }
40187 }
40188 }
40189
40190 /* Send data to the client from our intermediary buffer. */
40191 _ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcess, device.intermediaryBuffer);
40192
40193 totalFramesProcessed += framesToProcess;
40194 }
40195 };
40196
40197 navigator.mediaDevices.getUserMedia({audio:true, video:false})
40198 .then(function(stream) {
40199 device.streamNode = device.webaudio.createMediaStreamSource(stream);
40200 device.streamNode.connect(device.scriptNode);
40201 device.scriptNode.connect(device.webaudio.destination);
40202 })
40203 .catch(function(error) {
40204 /* I think this should output silence... */
40205 device.scriptNode.connect(device.webaudio.destination);
40206 });
40207 } else {
40208 device.scriptNode.onaudioprocess = function(e) {
40209 if (device.intermediaryBuffer === undefined) {
40210 return; /* This means the device has been uninitialized. */
40211 }
40212
40213 if(device.intermediaryBufferView.length == 0) {
40214 /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */
40215 device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
40216 }
40217
40218 var outputSilence = false;
40219
40220 /* Sanity check. This will never happen, right? */
40221 if (e.outputBuffer.numberOfChannels != channels) {
40222 console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
40223 outputSilence = true;
40224 return;
40225 }
40226
40227 /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
40228 var totalFramesProcessed = 0;
40229 while (totalFramesProcessed < e.outputBuffer.length) {
40230 var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
40231 var framesToProcess = framesRemaining;
40232 if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
40233 framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
40234 }
40235
40236 /* Read data from the client into our intermediary buffer. */
40237 _ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcess, device.intermediaryBuffer);
40238
40239 /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
40240 if (outputSilence) {
40241 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
40242 e.outputBuffer.getChannelData(iChannel).fill(0.0);
40243 }
40244 } else {
40245 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
40246 var outputBuffer = e.outputBuffer.getChannelData(iChannel);
40247 var intermediaryBuffer = device.intermediaryBufferView;
40248 for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
40249 outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel];
40250 }
40251 }
40252 }
40253
40254 totalFramesProcessed += framesToProcess;
40255 }
40256 };
40257
40258 device.scriptNode.connect(device.webaudio.destination);
40259 }
40260
40261 return miniaudio.track_device(device);
40262 }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice, &pDevice->pContext->allocationCallbacks);
40263
40264 if (deviceIndex < 0) {
40265 return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
40266 }
40267 #endif
40268
40269 #if defined(MA_USE_AUDIO_WORKLETS)
40270 if (deviceType == ma_device_type_capture) {
40271 pDevice->webaudio.audioContextCapture = audioContext;
40272 pDevice->webaudio.pStackBufferCapture = pStackBuffer;
40273 pDevice->webaudio.intermediaryBufferSizeInFramesCapture = intermediaryBufferSizeInFrames;
40274 pDevice->webaudio.pIntermediaryBufferCapture = pIntermediaryBuffer;
40275 } else {
40276 pDevice->webaudio.audioContextPlayback = audioContext;
40277 pDevice->webaudio.pStackBufferPlayback = pStackBuffer;
40278 pDevice->webaudio.intermediaryBufferSizeInFramesPlayback = intermediaryBufferSizeInFrames;
40279 pDevice->webaudio.pIntermediaryBufferPlayback = pIntermediaryBuffer;
40280 }
40281 #else
40282 if (deviceType == ma_device_type_capture) {
40283 pDevice->webaudio.indexCapture = deviceIndex;
40284 } else {
40285 pDevice->webaudio.indexPlayback = deviceIndex;
40286 }
40287 #endif
40288
40289 pDescriptor->format = ma_format_f32;
40290 pDescriptor->channels = channels;
40291 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
40292 pDescriptor->periodSizeInFrames = periodSizeInFrames;
40293 pDescriptor->periodCount = 1;
40294
40295 #if defined(MA_USE_AUDIO_WORKLETS)
40296 pDescriptor->sampleRate = sampleRate; /* Is this good enough to be used in the general case? */
40297 #else
40298 pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
40299 #endif
40300
40301 return MA_SUCCESS;
40302 }
40303
40304 static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
40305 {
40306 ma_result result;
40307
40308 if (pConfig->deviceType == ma_device_type_loopback) {
40309 return MA_DEVICE_TYPE_NOT_SUPPORTED;
40310 }
40311
40312 /* No exclusive mode with Web Audio. */
40313 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
40314 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
40315 return MA_SHARE_MODE_NOT_SUPPORTED;
40316 }
40317
40318 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
40319 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
40320 if (result != MA_SUCCESS) {
40321 return result;
40322 }
40323 }
40324
40325 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
40326 result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
40327 if (result != MA_SUCCESS) {
40328 if (pConfig->deviceType == ma_device_type_duplex) {
40329 ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture);
40330 }
40331 return result;
40332 }
40333 }
40334
40335 return MA_SUCCESS;
40336 }
40337
40338 static ma_result ma_device_start__webaudio(ma_device* pDevice)
40339 {
40340 MA_ASSERT(pDevice != NULL);
40341
40342 #if defined(MA_USE_AUDIO_WORKLETS)
40343 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
40344 emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextCapture);
40345 }
40346
40347 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
40348 emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextPlayback);
40349 }
40350 #else
40351 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
40352 EM_ASM({
40353 var device = miniaudio.get_device_by_index($0);
40354 device.webaudio.resume();
40355 device.state = 2; /* ma_device_state_started */
40356 }, pDevice->webaudio.indexCapture);
40357 }
40358
40359 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
40360 EM_ASM({
40361 var device = miniaudio.get_device_by_index($0);
40362 device.webaudio.resume();
40363 device.state = 2; /* ma_device_state_started */
40364 }, pDevice->webaudio.indexPlayback);
40365 }
40366 #endif
40367
40368 return MA_SUCCESS;
40369 }
40370
40371 static ma_result ma_device_stop__webaudio(ma_device* pDevice)
40372 {
40373 MA_ASSERT(pDevice != NULL);
40374
40375 /*
40376 From the WebAudio API documentation for AudioContext.suspend():
40377
40378 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
40379 destination, and then allows the system to release its claim on audio hardware.
40380
40381 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
40382 do any kind of explicit draining.
40383 */
40384
40385 #if defined(MA_USE_AUDIO_WORKLETS)
40386 /* I can't seem to find a way to suspend an AudioContext via the C Emscripten API. Is this an oversight? */
40387 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
40388 EM_ASM({
40389 emscriptenGetAudioObject($0).suspend();
40390 }, pDevice->webaudio.audioContextCapture);
40391 }
40392
40393 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
40394 EM_ASM({
40395 emscriptenGetAudioObject($0).suspend();
40396 }, pDevice->webaudio.audioContextPlayback);
40397 }
40398 #else
40399 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
40400 EM_ASM({
40401 var device = miniaudio.get_device_by_index($0);
40402 device.webaudio.suspend();
40403 device.state = 1; /* ma_device_state_stopped */
40404 }, pDevice->webaudio.indexCapture);
40405 }
40406
40407 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
40408 EM_ASM({
40409 var device = miniaudio.get_device_by_index($0);
40410 device.webaudio.suspend();
40411 device.state = 1; /* ma_device_state_stopped */
40412 }, pDevice->webaudio.indexPlayback);
40413 }
40414 #endif
40415
40416 ma_device__on_notification_stopped(pDevice);
40417
40418 return MA_SUCCESS;
40419 }
40420
40421 static ma_result ma_context_uninit__webaudio(ma_context* pContext)
40422 {
40423 MA_ASSERT(pContext != NULL);
40424 MA_ASSERT(pContext->backend == ma_backend_webaudio);
40425
40426 (void)pContext; /* Unused. */
40427
40428 /* Remove the global miniaudio object from window if there are no more references to it. */
40429 EM_ASM({
40430 if (typeof(window.miniaudio) !== 'undefined') {
40431 window.miniaudio.referenceCount--;
40432 if (window.miniaudio.referenceCount === 0) {
40433 delete window.miniaudio;
40434 }
40435 }
40436 });
40437
40438 return MA_SUCCESS;
40439 }
40440
40441 static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
40442 {
40443 int resultFromJS;
40444
40445 MA_ASSERT(pContext != NULL);
40446
40447 (void)pConfig; /* Unused. */
40448
40449 /* Here is where our global JavaScript object is initialized. */
40450 resultFromJS = EM_ASM_INT({
40451 if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) {
40452 return 0; /* Web Audio not supported. */
40453 }
40454
40455 if (typeof(window.miniaudio) === 'undefined') {
40456 window.miniaudio = {
40457 referenceCount: 0
40458 };
40459 miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
40460
40461 miniaudio.track_device = function(device) {
40462 /* Try inserting into a free slot first. */
40463 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
40464 if (miniaudio.devices[iDevice] == null) {
40465 miniaudio.devices[iDevice] = device;
40466 return iDevice;
40467 }
40468 }
40469
40470 /* Getting here means there is no empty slots in the array so we just push to the end. */
40471 miniaudio.devices.push(device);
40472 return miniaudio.devices.length - 1;
40473 };
40474
40475 miniaudio.untrack_device_by_index = function(deviceIndex) {
40476 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
40477 miniaudio.devices[deviceIndex] = null;
40478
40479 /* Trim the array if possible. */
40480 while (miniaudio.devices.length > 0) {
40481 if (miniaudio.devices[miniaudio.devices.length-1] == null) {
40482 miniaudio.devices.pop();
40483 } else {
40484 break;
40485 }
40486 }
40487 };
40488
40489 miniaudio.untrack_device = function(device) {
40490 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
40491 if (miniaudio.devices[iDevice] == device) {
40492 return miniaudio.untrack_device_by_index(iDevice);
40493 }
40494 }
40495 };
40496
40497 miniaudio.get_device_by_index = function(deviceIndex) {
40498 return miniaudio.devices[deviceIndex];
40499 };
40500
40501 miniaudio.unlock_event_types = (function(){
40502 return ['touchstart', 'touchend', 'click'];
40503 })();
40504
40505 miniaudio.unlock = function() {
40506 for(var i = 0; i < miniaudio.devices.length; ++i) {
40507 var device = miniaudio.devices[i];
40508 if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) {
40509 device.webaudio.resume();
40510 }
40511 }
40512 miniaudio.unlock_event_types.map(function(event_type) {
40513 document.removeEventListener(event_type, miniaudio.unlock, true);
40514 });
40515 };
40516
40517 miniaudio.unlock_event_types.map(function(event_type) {
40518 document.addEventListener(event_type, miniaudio.unlock, true);
40519 });
40520 }
40521
40522 window.miniaudio.referenceCount++;
40523
40524 return 1;
40525 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
40526
40527 if (resultFromJS != 1) {
40528 return MA_FAILED_TO_INIT_BACKEND;
40529 }
40530
40531 pCallbacks->onContextInit = ma_context_init__webaudio;
40532 pCallbacks->onContextUninit = ma_context_uninit__webaudio;
40533 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;
40534 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio;
40535 pCallbacks->onDeviceInit = ma_device_init__webaudio;
40536 pCallbacks->onDeviceUninit = ma_device_uninit__webaudio;
40537 pCallbacks->onDeviceStart = ma_device_start__webaudio;
40538 pCallbacks->onDeviceStop = ma_device_stop__webaudio;
40539 pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */
40540 pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */
40541 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */
40542
40543 return MA_SUCCESS;
40544 }
40545 #endif /* Web Audio */
40546
40547
40548
40549 static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels)
40550 {
40551 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
40552 if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) {
40553 ma_uint32 iChannel;
40554
40555 if (channels == 0 || channels > MA_MAX_CHANNELS) {
40556 return MA_FALSE; /* Channel count out of range. */
40557 }
40558
40559 /* A channel cannot be present in the channel map more than once. */
40560 for (iChannel = 0; iChannel < channels; ++iChannel) {
40561 ma_uint32 jChannel;
40562 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
40563 if (pChannelMap[iChannel] == pChannelMap[jChannel]) {
40564 return MA_FALSE;
40565 }
40566 }
40567 }
40568 }
40569
40570 return MA_TRUE;
40571 }
40572
40573
40574 static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
40575 {
40576 MA_ASSERT(pContext != NULL);
40577
40578 if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
40579 if (pContext->callbacks.onDeviceDataLoop == NULL) {
40580 return MA_TRUE;
40581 } else {
40582 return MA_FALSE;
40583 }
40584 } else {
40585 return MA_FALSE;
40586 }
40587 }
40588
40589
40590 static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
40591 {
40592 ma_result result;
40593
40594 MA_ASSERT(pDevice != NULL);
40595
40596 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40597 if (pDevice->capture.format == ma_format_unknown) {
40598 pDevice->capture.format = pDevice->capture.internalFormat;
40599 }
40600 if (pDevice->capture.channels == 0) {
40601 pDevice->capture.channels = pDevice->capture.internalChannels;
40602 }
40603 if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {
40604 MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
40605 if (pDevice->capture.internalChannels == pDevice->capture.channels) {
40606 ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
40607 } else {
40608 if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) {
40609 ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels);
40610 } else {
40611 ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels);
40612 }
40613 }
40614 }
40615 }
40616
40617 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40618 if (pDevice->playback.format == ma_format_unknown) {
40619 pDevice->playback.format = pDevice->playback.internalFormat;
40620 }
40621 if (pDevice->playback.channels == 0) {
40622 pDevice->playback.channels = pDevice->playback.internalChannels;
40623 }
40624 if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {
40625 MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
40626 if (pDevice->playback.internalChannels == pDevice->playback.channels) {
40627 ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
40628 } else {
40629 if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) {
40630 ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels);
40631 } else {
40632 ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels);
40633 }
40634 }
40635 }
40636 }
40637
40638 if (pDevice->sampleRate == 0) {
40639 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40640 pDevice->sampleRate = pDevice->capture.internalSampleRate;
40641 } else {
40642 pDevice->sampleRate = pDevice->playback.internalSampleRate;
40643 }
40644 }
40645
40646 /* Data converters. */
40647 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40648 /* Converting from internal device format to client format. */
40649 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
40650 converterConfig.formatIn = pDevice->capture.internalFormat;
40651 converterConfig.channelsIn = pDevice->capture.internalChannels;
40652 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
40653 converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap;
40654 converterConfig.formatOut = pDevice->capture.format;
40655 converterConfig.channelsOut = pDevice->capture.channels;
40656 converterConfig.sampleRateOut = pDevice->sampleRate;
40657 converterConfig.pChannelMapOut = pDevice->capture.channelMap;
40658 converterConfig.channelMixMode = pDevice->capture.channelMixMode;
40659 converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels;
40660 converterConfig.allowDynamicSampleRate = MA_FALSE;
40661 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
40662 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
40663 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
40664 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
40665
40666 /* Make sure the old converter is uninitialized first. */
40667 if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {
40668 ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);
40669 }
40670
40671 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter);
40672 if (result != MA_SUCCESS) {
40673 return result;
40674 }
40675 }
40676
40677 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40678 /* Converting from client format to device format. */
40679 ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
40680 converterConfig.formatIn = pDevice->playback.format;
40681 converterConfig.channelsIn = pDevice->playback.channels;
40682 converterConfig.sampleRateIn = pDevice->sampleRate;
40683 converterConfig.pChannelMapIn = pDevice->playback.channelMap;
40684 converterConfig.formatOut = pDevice->playback.internalFormat;
40685 converterConfig.channelsOut = pDevice->playback.internalChannels;
40686 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
40687 converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap;
40688 converterConfig.channelMixMode = pDevice->playback.channelMixMode;
40689 converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels;
40690 converterConfig.allowDynamicSampleRate = MA_FALSE;
40691 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
40692 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
40693 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
40694 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
40695
40696 /* Make sure the old converter is uninitialized first. */
40697 if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {
40698 ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);
40699 }
40700
40701 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter);
40702 if (result != MA_SUCCESS) {
40703 return result;
40704 }
40705 }
40706
40707
40708 /*
40709 If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's
40710 a couple of situations where we'll need a heap allocated cache.
40711
40712 The first is a duplex device for backends that use a callback for data delivery. The reason
40713 this is needed is that the input stage needs to have a buffer to place the input data while it
40714 waits for the playback stage, after which the miniaudio data callback will get fired. This is
40715 not needed for backends that use a blocking API because miniaudio manages temporary buffers on
40716 the stack to achieve this.
40717
40718 The other situation is when the data converter does not have the ability to query the number
40719 of input frames that are required in order to process a given number of output frames. When
40720 performing data conversion, it's useful if miniaudio know exactly how many frames it needs
40721 from the client in order to generate a given number of output frames. This way, only exactly
40722 the number of frames are needed to be read from the client which means no cache is necessary.
40723 On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read
40724 in fixed sized chunks and then cache any residual unused input frames, those of which will be
40725 processed at a later stage.
40726 */
40727 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40728 ma_uint64 unused;
40729
40730 pDevice->playback.inputCacheConsumed = 0;
40731 pDevice->playback.inputCacheRemaining = 0;
40732
40733 if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */
40734 ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */
40735 {
40736 /* We need a heap allocated cache. We want to size this based on the period size. */
40737 void* pNewInputCache;
40738 ma_uint64 newInputCacheCap;
40739 ma_uint64 newInputCacheSizeInBytes;
40740
40741 newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames);
40742
40743 newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
40744 if (newInputCacheSizeInBytes > MA_SIZE_MAX) {
40745 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
40746 pDevice->playback.pInputCache = NULL;
40747 pDevice->playback.inputCacheCap = 0;
40748 return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */
40749 }
40750
40751 pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks);
40752 if (pNewInputCache == NULL) {
40753 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
40754 pDevice->playback.pInputCache = NULL;
40755 pDevice->playback.inputCacheCap = 0;
40756 return MA_OUT_OF_MEMORY;
40757 }
40758
40759 pDevice->playback.pInputCache = pNewInputCache;
40760 pDevice->playback.inputCacheCap = newInputCacheCap;
40761 } else {
40762 /* 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. */
40763 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
40764 pDevice->playback.pInputCache = NULL;
40765 pDevice->playback.inputCacheCap = 0;
40766 }
40767 }
40768
40769 return MA_SUCCESS;
40770 }
40771
40772 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)
40773 {
40774 ma_result result;
40775
40776 if (pDevice == NULL) {
40777 return MA_INVALID_ARGS;
40778 }
40779
40780 /* Capture. */
40781 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40782 if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) {
40783 return MA_INVALID_ARGS;
40784 }
40785
40786 pDevice->capture.internalFormat = pDescriptorCapture->format;
40787 pDevice->capture.internalChannels = pDescriptorCapture->channels;
40788 pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate;
40789 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
40790 pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
40791 pDevice->capture.internalPeriods = pDescriptorCapture->periodCount;
40792
40793 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
40794 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate);
40795 }
40796 }
40797
40798 /* Playback. */
40799 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40800 if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) {
40801 return MA_INVALID_ARGS;
40802 }
40803
40804 pDevice->playback.internalFormat = pDescriptorPlayback->format;
40805 pDevice->playback.internalChannels = pDescriptorPlayback->channels;
40806 pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate;
40807 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
40808 pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
40809 pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount;
40810
40811 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
40812 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate);
40813 }
40814 }
40815
40816 /*
40817 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.
40818 For loopback devices, we need to retrieve the name of the playback device.
40819 */
40820 {
40821 ma_device_info deviceInfo;
40822
40823 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
40824 result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
40825 if (result == MA_SUCCESS) {
40826 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
40827 } else {
40828 /* We failed to retrieve the device info. Fall back to a default name. */
40829 if (pDescriptorCapture->pDeviceID == NULL) {
40830 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
40831 } else {
40832 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
40833 }
40834 }
40835 }
40836
40837 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
40838 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
40839 if (result == MA_SUCCESS) {
40840 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
40841 } else {
40842 /* We failed to retrieve the device info. Fall back to a default name. */
40843 if (pDescriptorPlayback->pDeviceID == NULL) {
40844 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
40845 } else {
40846 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
40847 }
40848 }
40849 }
40850 }
40851
40852 /* Update data conversion. */
40853 return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */
40854 }
40855
40856
40857 static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
40858 {
40859 ma_device* pDevice = (ma_device*)pData;
40860 MA_ASSERT(pDevice != NULL);
40861
40862 #ifdef MA_WIN32
40863 ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
40864 #endif
40865
40866 /*
40867 When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from
40868 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
40869 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
40870 thread to signal an event to know when the worker thread is ready for action.
40871 */
40872 ma_device__set_state(pDevice, ma_device_state_stopped);
40873 ma_event_signal(&pDevice->stopEvent);
40874
40875 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
40876 ma_result startResult;
40877 ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */
40878
40879 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
40880 ma_event_wait(&pDevice->wakeupEvent);
40881
40882 /* Default result code. */
40883 pDevice->workResult = MA_SUCCESS;
40884
40885 /* If the reason for the wake up is that we are terminating, just break from the loop. */
40886 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
40887 break;
40888 }
40889
40890 /*
40891 Getting to this point means the device is wanting to get started. The function that has requested that the device
40892 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
40893 in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
40894 */
40895 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting);
40896
40897 /* If the device has a start callback, start it now. */
40898 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
40899 startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);
40900 } else {
40901 startResult = MA_SUCCESS;
40902 }
40903
40904 /*
40905 If starting was not successful we'll need to loop back to the start and wait for something
40906 to happen (pDevice->wakeupEvent).
40907 */
40908 if (startResult != MA_SUCCESS) {
40909 pDevice->workResult = startResult;
40910 ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */
40911 continue;
40912 }
40913
40914 /* Make sure the state is set appropriately. */
40915 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. */
40916 ma_event_signal(&pDevice->startEvent);
40917
40918 ma_device__on_notification_started(pDevice);
40919
40920 if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
40921 pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
40922 } else {
40923 /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
40924 ma_device_audio_thread__default_read_write(pDevice);
40925 }
40926
40927 /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */
40928 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
40929 stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);
40930 } else {
40931 stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */
40932 }
40933
40934 /*
40935 After the device has stopped, make sure an event is posted. Don't post a stopped event if
40936 stopping failed. This can happen on some backends when the underlying stream has been
40937 stopped due to the device being physically unplugged or disabled via an OS setting.
40938 */
40939 if (stopResult == MA_SUCCESS) {
40940 ma_device__on_notification_stopped(pDevice);
40941 }
40942
40943 /* 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. */
40944 ma_device__set_state(pDevice, ma_device_state_stopped);
40945 ma_event_signal(&pDevice->stopEvent);
40946 }
40947
40948 #ifdef MA_WIN32
40949 ma_CoUninitialize(pDevice->pContext);
40950 #endif
40951
40952 return (ma_thread_result)0;
40953 }
40954
40955
40956 /* Helper for determining whether or not the given device is initialized. */
40957 static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
40958 {
40959 if (pDevice == NULL) {
40960 return MA_FALSE;
40961 }
40962
40963 return ma_device_get_state(pDevice) != ma_device_state_uninitialized;
40964 }
40965
40966
40967 #ifdef MA_WIN32
40968 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
40969 {
40970 /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */
40971 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
40972 ma_CoUninitialize(pContext);
40973
40974 #if defined(MA_WIN32_DESKTOP)
40975 ma_dlclose(pContext, pContext->win32.hUser32DLL);
40976 ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
40977 #endif
40978
40979 ma_dlclose(pContext, pContext->win32.hOle32DLL);
40980 #else
40981 (void)pContext;
40982 #endif
40983
40984 return MA_SUCCESS;
40985 }
40986
40987 static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
40988 {
40989 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
40990 #if defined(MA_WIN32_DESKTOP)
40991 /* User32.dll */
40992 pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
40993 if (pContext->win32.hUser32DLL == NULL) {
40994 return MA_FAILED_TO_INIT_BACKEND;
40995 }
40996
40997 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
40998 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
40999
41000
41001 /* Advapi32.dll */
41002 pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
41003 if (pContext->win32.hAdvapi32DLL == NULL) {
41004 return MA_FAILED_TO_INIT_BACKEND;
41005 }
41006
41007 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
41008 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
41009 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
41010 #endif
41011
41012 /* Ole32.dll */
41013 pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
41014 if (pContext->win32.hOle32DLL == NULL) {
41015 return MA_FAILED_TO_INIT_BACKEND;
41016 }
41017
41018 pContext->win32.CoInitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitialize");
41019 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
41020 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
41021 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
41022 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
41023 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
41024 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
41025 #else
41026 (void)pContext; /* Unused. */
41027 #endif
41028
41029 ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
41030 return MA_SUCCESS;
41031 }
41032 #else
41033 static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
41034 {
41035 (void)pContext;
41036
41037 return MA_SUCCESS;
41038 }
41039
41040 static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
41041 {
41042 (void)pContext;
41043
41044 return MA_SUCCESS;
41045 }
41046 #endif
41047
41048 static ma_result ma_context_init_backend_apis(ma_context* pContext)
41049 {
41050 ma_result result;
41051 #ifdef MA_WIN32
41052 result = ma_context_init_backend_apis__win32(pContext);
41053 #else
41054 result = ma_context_init_backend_apis__nix(pContext);
41055 #endif
41056
41057 return result;
41058 }
41059
41060 static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
41061 {
41062 ma_result result;
41063 #ifdef MA_WIN32
41064 result = ma_context_uninit_backend_apis__win32(pContext);
41065 #else
41066 result = ma_context_uninit_backend_apis__nix(pContext);
41067 #endif
41068
41069 return result;
41070 }
41071
41072
41073 /* The default capacity doesn't need to be too big. */
41074 #ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY
41075 #define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32
41076 #endif
41077
41078 MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void)
41079 {
41080 ma_device_job_thread_config config;
41081
41082 MA_ZERO_OBJECT(&config);
41083 config.noThread = MA_FALSE;
41084 config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY;
41085 config.jobQueueFlags = 0;
41086
41087 return config;
41088 }
41089
41090
41091 static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData)
41092 {
41093 ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData;
41094 MA_ASSERT(pJobThread != NULL);
41095
41096 for (;;) {
41097 ma_result result;
41098 ma_job job;
41099
41100 result = ma_device_job_thread_next(pJobThread, &job);
41101 if (result != MA_SUCCESS) {
41102 break;
41103 }
41104
41105 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
41106 break;
41107 }
41108
41109 ma_job_process(&job);
41110 }
41111
41112 return (ma_thread_result)0;
41113 }
41114
41115 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)
41116 {
41117 ma_result result;
41118 ma_job_queue_config jobQueueConfig;
41119
41120 if (pJobThread == NULL) {
41121 return MA_INVALID_ARGS;
41122 }
41123
41124 MA_ZERO_OBJECT(pJobThread);
41125
41126 if (pConfig == NULL) {
41127 return MA_INVALID_ARGS;
41128 }
41129
41130
41131 /* Initialize the job queue before the thread to ensure it's in a valid state. */
41132 jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity);
41133
41134 result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue);
41135 if (result != MA_SUCCESS) {
41136 return result; /* Failed to initialize job queue. */
41137 }
41138
41139
41140 /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */
41141 if (pConfig->noThread == MA_FALSE) {
41142 result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks);
41143 if (result != MA_SUCCESS) {
41144 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
41145 return result; /* Failed to create the job thread. */
41146 }
41147
41148 pJobThread->_hasThread = MA_TRUE;
41149 } else {
41150 pJobThread->_hasThread = MA_FALSE;
41151 }
41152
41153
41154 return MA_SUCCESS;
41155 }
41156
41157 MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks)
41158 {
41159 if (pJobThread == NULL) {
41160 return;
41161 }
41162
41163 /* 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. */
41164 {
41165 ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
41166 ma_device_job_thread_post(pJobThread, &job);
41167 }
41168
41169 /* Wait for the thread to terminate naturally. */
41170 if (pJobThread->_hasThread) {
41171 ma_thread_wait(&pJobThread->thread);
41172 }
41173
41174 /* At this point the thread should be terminated so we can safely uninitialize the job queue. */
41175 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
41176 }
41177
41178 MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob)
41179 {
41180 if (pJobThread == NULL || pJob == NULL) {
41181 return MA_INVALID_ARGS;
41182 }
41183
41184 return ma_job_queue_post(&pJobThread->jobQueue, pJob);
41185 }
41186
41187 MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob)
41188 {
41189 if (pJob == NULL) {
41190 return MA_INVALID_ARGS;
41191 }
41192
41193 MA_ZERO_OBJECT(pJob);
41194
41195 if (pJobThread == NULL) {
41196 return MA_INVALID_ARGS;
41197 }
41198
41199 return ma_job_queue_next(&pJobThread->jobQueue, pJob);
41200 }
41201
41202
41203
41204 MA_API ma_context_config ma_context_config_init(void)
41205 {
41206 ma_context_config config;
41207 MA_ZERO_OBJECT(&config);
41208
41209 return config;
41210 }
41211
41212 MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
41213 {
41214 ma_result result;
41215 ma_context_config defaultConfig;
41216 ma_backend defaultBackends[ma_backend_null+1];
41217 ma_uint32 iBackend;
41218 ma_backend* pBackendsToIterate;
41219 ma_uint32 backendsToIterateCount;
41220
41221 if (pContext == NULL) {
41222 return MA_INVALID_ARGS;
41223 }
41224
41225 MA_ZERO_OBJECT(pContext);
41226
41227 /* Always make sure the config is set first to ensure properties are available as soon as possible. */
41228 if (pConfig == NULL) {
41229 defaultConfig = ma_context_config_init();
41230 pConfig = &defaultConfig;
41231 }
41232
41233 /* Allocation callbacks need to come first because they'll be passed around to other areas. */
41234 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
41235 if (result != MA_SUCCESS) {
41236 return result;
41237 }
41238
41239 /* Get a lot set up first so we can start logging ASAP. */
41240 if (pConfig->pLog != NULL) {
41241 pContext->pLog = pConfig->pLog;
41242 } else {
41243 result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);
41244 if (result == MA_SUCCESS) {
41245 pContext->pLog = &pContext->log;
41246 } else {
41247 pContext->pLog = NULL; /* Logging is not available. */
41248 }
41249 }
41250
41251 pContext->threadPriority = pConfig->threadPriority;
41252 pContext->threadStackSize = pConfig->threadStackSize;
41253 pContext->pUserData = pConfig->pUserData;
41254
41255 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
41256 result = ma_context_init_backend_apis(pContext);
41257 if (result != MA_SUCCESS) {
41258 return result;
41259 }
41260
41261 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
41262 defaultBackends[iBackend] = (ma_backend)iBackend;
41263 }
41264
41265 pBackendsToIterate = (ma_backend*)backends;
41266 backendsToIterateCount = backendCount;
41267 if (pBackendsToIterate == NULL) {
41268 pBackendsToIterate = (ma_backend*)defaultBackends;
41269 backendsToIterateCount = ma_countof(defaultBackends);
41270 }
41271
41272 MA_ASSERT(pBackendsToIterate != NULL);
41273
41274 for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
41275 ma_backend backend = pBackendsToIterate[iBackend];
41276
41277 /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */
41278 MA_ZERO_OBJECT(&pContext->callbacks);
41279
41280 /* These backends are using the new callback system. */
41281 switch (backend) {
41282 #ifdef MA_HAS_WASAPI
41283 case ma_backend_wasapi:
41284 {
41285 pContext->callbacks.onContextInit = ma_context_init__wasapi;
41286 } break;
41287 #endif
41288 #ifdef MA_HAS_DSOUND
41289 case ma_backend_dsound:
41290 {
41291 pContext->callbacks.onContextInit = ma_context_init__dsound;
41292 } break;
41293 #endif
41294 #ifdef MA_HAS_WINMM
41295 case ma_backend_winmm:
41296 {
41297 pContext->callbacks.onContextInit = ma_context_init__winmm;
41298 } break;
41299 #endif
41300 #ifdef MA_HAS_COREAUDIO
41301 case ma_backend_coreaudio:
41302 {
41303 pContext->callbacks.onContextInit = ma_context_init__coreaudio;
41304 } break;
41305 #endif
41306 #ifdef MA_HAS_SNDIO
41307 case ma_backend_sndio:
41308 {
41309 pContext->callbacks.onContextInit = ma_context_init__sndio;
41310 } break;
41311 #endif
41312 #ifdef MA_HAS_AUDIO4
41313 case ma_backend_audio4:
41314 {
41315 pContext->callbacks.onContextInit = ma_context_init__audio4;
41316 } break;
41317 #endif
41318 #ifdef MA_HAS_OSS
41319 case ma_backend_oss:
41320 {
41321 pContext->callbacks.onContextInit = ma_context_init__oss;
41322 } break;
41323 #endif
41324 #ifdef MA_HAS_PULSEAUDIO
41325 case ma_backend_pulseaudio:
41326 {
41327 pContext->callbacks.onContextInit = ma_context_init__pulse;
41328 } break;
41329 #endif
41330 #ifdef MA_HAS_ALSA
41331 case ma_backend_alsa:
41332 {
41333 pContext->callbacks.onContextInit = ma_context_init__alsa;
41334 } break;
41335 #endif
41336 #ifdef MA_HAS_JACK
41337 case ma_backend_jack:
41338 {
41339 pContext->callbacks.onContextInit = ma_context_init__jack;
41340 } break;
41341 #endif
41342 #ifdef MA_HAS_AAUDIO
41343 case ma_backend_aaudio:
41344 {
41345 if (ma_is_backend_enabled(backend)) {
41346 pContext->callbacks.onContextInit = ma_context_init__aaudio;
41347 }
41348 } break;
41349 #endif
41350 #ifdef MA_HAS_OPENSL
41351 case ma_backend_opensl:
41352 {
41353 if (ma_is_backend_enabled(backend)) {
41354 pContext->callbacks.onContextInit = ma_context_init__opensl;
41355 }
41356 } break;
41357 #endif
41358 #ifdef MA_HAS_WEBAUDIO
41359 case ma_backend_webaudio:
41360 {
41361 pContext->callbacks.onContextInit = ma_context_init__webaudio;
41362 } break;
41363 #endif
41364 #ifdef MA_HAS_CUSTOM
41365 case ma_backend_custom:
41366 {
41367 /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
41368 pContext->callbacks = pConfig->custom;
41369 } break;
41370 #endif
41371 #ifdef MA_HAS_NULL
41372 case ma_backend_null:
41373 {
41374 pContext->callbacks.onContextInit = ma_context_init__null;
41375 } break;
41376 #endif
41377
41378 default: break;
41379 }
41380
41381 if (pContext->callbacks.onContextInit != NULL) {
41382 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend));
41383 result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
41384 } else {
41385 /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */
41386 if (backend != ma_backend_custom) {
41387 result = MA_BACKEND_NOT_ENABLED;
41388 } else {
41389 #if !defined(MA_HAS_CUSTOM)
41390 result = MA_BACKEND_NOT_ENABLED;
41391 #else
41392 result = MA_NO_BACKEND;
41393 #endif
41394 }
41395 }
41396
41397 /* If this iteration was successful, return. */
41398 if (result == MA_SUCCESS) {
41399 result = ma_mutex_init(&pContext->deviceEnumLock);
41400 if (result != MA_SUCCESS) {
41401 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");
41402 }
41403
41404 result = ma_mutex_init(&pContext->deviceInfoLock);
41405 if (result != MA_SUCCESS) {
41406 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");
41407 }
41408
41409 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n");
41410 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
41411 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
41412 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
41413 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO");
41414
41415 pContext->backend = backend;
41416 return result;
41417 } else {
41418 if (result == MA_BACKEND_NOT_ENABLED) {
41419 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend));
41420 } else {
41421 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend));
41422 }
41423 }
41424 }
41425
41426 /* If we get here it means an error occurred. */
41427 MA_ZERO_OBJECT(pContext); /* Safety. */
41428 return MA_NO_BACKEND;
41429 }
41430
41431 MA_API ma_result ma_context_uninit(ma_context* pContext)
41432 {
41433 if (pContext == NULL) {
41434 return MA_INVALID_ARGS;
41435 }
41436
41437 if (pContext->callbacks.onContextUninit != NULL) {
41438 pContext->callbacks.onContextUninit(pContext);
41439 }
41440
41441 ma_mutex_uninit(&pContext->deviceEnumLock);
41442 ma_mutex_uninit(&pContext->deviceInfoLock);
41443 ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks);
41444 ma_context_uninit_backend_apis(pContext);
41445
41446 if (pContext->pLog == &pContext->log) {
41447 ma_log_uninit(&pContext->log);
41448 }
41449
41450 return MA_SUCCESS;
41451 }
41452
41453 MA_API size_t ma_context_sizeof(void)
41454 {
41455 return sizeof(ma_context);
41456 }
41457
41458
41459 MA_API ma_log* ma_context_get_log(ma_context* pContext)
41460 {
41461 if (pContext == NULL) {
41462 return NULL;
41463 }
41464
41465 return pContext->pLog;
41466 }
41467
41468
41469 MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
41470 {
41471 ma_result result;
41472
41473 if (pContext == NULL || callback == NULL) {
41474 return MA_INVALID_ARGS;
41475 }
41476
41477 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
41478 return MA_INVALID_OPERATION;
41479 }
41480
41481 ma_mutex_lock(&pContext->deviceEnumLock);
41482 {
41483 result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
41484 }
41485 ma_mutex_unlock(&pContext->deviceEnumLock);
41486
41487 return result;
41488 }
41489
41490
41491 static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
41492 {
41493 /*
41494 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
41495 it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
41496 */
41497
41498 /*
41499 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
41500 simple fixed size increment for buffer expansion.
41501 */
41502 const ma_uint32 bufferExpansionCount = 2;
41503 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
41504
41505 if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {
41506 ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount;
41507 ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks);
41508 if (pNewInfos == NULL) {
41509 return MA_FALSE; /* Out of memory. */
41510 }
41511
41512 pContext->pDeviceInfos = pNewInfos;
41513 pContext->deviceInfoCapacity = newCapacity;
41514 }
41515
41516 if (deviceType == ma_device_type_playback) {
41517 /* Playback. Insert just before the first capture device. */
41518
41519 /* The first thing to do is move all of the capture devices down a slot. */
41520 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
41521 size_t iCaptureDevice;
41522 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
41523 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
41524 }
41525
41526 /* Now just insert where the first capture device was before moving it down a slot. */
41527 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
41528 pContext->playbackDeviceInfoCount += 1;
41529 } else {
41530 /* Capture. Insert at the end. */
41531 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
41532 pContext->captureDeviceInfoCount += 1;
41533 }
41534
41535 (void)pUserData;
41536 return MA_TRUE;
41537 }
41538
41539 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)
41540 {
41541 ma_result result;
41542
41543 /* Safety. */
41544 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
41545 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
41546 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
41547 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
41548
41549 if (pContext == NULL) {
41550 return MA_INVALID_ARGS;
41551 }
41552
41553 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
41554 return MA_INVALID_OPERATION;
41555 }
41556
41557 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
41558 ma_mutex_lock(&pContext->deviceEnumLock);
41559 {
41560 /* Reset everything first. */
41561 pContext->playbackDeviceInfoCount = 0;
41562 pContext->captureDeviceInfoCount = 0;
41563
41564 /* Now enumerate over available devices. */
41565 result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
41566 if (result == MA_SUCCESS) {
41567 /* Playback devices. */
41568 if (ppPlaybackDeviceInfos != NULL) {
41569 *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
41570 }
41571 if (pPlaybackDeviceCount != NULL) {
41572 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
41573 }
41574
41575 /* Capture devices. */
41576 if (ppCaptureDeviceInfos != NULL) {
41577 *ppCaptureDeviceInfos = pContext->pDeviceInfos;
41578 /* Capture devices come after playback devices. */
41579 if (pContext->playbackDeviceInfoCount > 0) {
41580 /* Conditional, because NULL+0 is undefined behavior. */
41581 *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount;
41582 }
41583 }
41584 if (pCaptureDeviceCount != NULL) {
41585 *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
41586 }
41587 }
41588 }
41589 ma_mutex_unlock(&pContext->deviceEnumLock);
41590
41591 return result;
41592 }
41593
41594 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)
41595 {
41596 ma_result result;
41597 ma_device_info deviceInfo;
41598
41599 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
41600 if (pContext == NULL || pDeviceInfo == NULL) {
41601 return MA_INVALID_ARGS;
41602 }
41603
41604 MA_ZERO_OBJECT(&deviceInfo);
41605
41606 /* Help the backend out by copying over the device ID if we have one. */
41607 if (pDeviceID != NULL) {
41608 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
41609 }
41610
41611 if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
41612 return MA_INVALID_OPERATION;
41613 }
41614
41615 ma_mutex_lock(&pContext->deviceInfoLock);
41616 {
41617 result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
41618 }
41619 ma_mutex_unlock(&pContext->deviceInfoLock);
41620
41621 *pDeviceInfo = deviceInfo;
41622 return result;
41623 }
41624
41625 MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
41626 {
41627 if (pContext == NULL) {
41628 return MA_FALSE;
41629 }
41630
41631 return ma_is_loopback_supported(pContext->backend);
41632 }
41633
41634
41635 MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
41636 {
41637 ma_device_config config;
41638 MA_ZERO_OBJECT(&config);
41639 config.deviceType = deviceType;
41640 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */
41641
41642 return config;
41643 }
41644
41645 MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
41646 {
41647 ma_result result;
41648 ma_device_descriptor descriptorPlayback;
41649 ma_device_descriptor descriptorCapture;
41650
41651 /* The context can be null, in which case we self-manage it. */
41652 if (pContext == NULL) {
41653 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
41654 }
41655
41656 if (pDevice == NULL) {
41657 return MA_INVALID_ARGS;
41658 }
41659
41660 MA_ZERO_OBJECT(pDevice);
41661
41662 if (pConfig == NULL) {
41663 return MA_INVALID_ARGS;
41664 }
41665
41666 /* Check that we have our callbacks defined. */
41667 if (pContext->callbacks.onDeviceInit == NULL) {
41668 return MA_INVALID_OPERATION;
41669 }
41670
41671 /* Basic config validation. */
41672 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
41673 if (pConfig->capture.channels > MA_MAX_CHANNELS) {
41674 return MA_INVALID_ARGS;
41675 }
41676
41677 if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) {
41678 return MA_INVALID_ARGS;
41679 }
41680 }
41681
41682 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41683 if (pConfig->playback.channels > MA_MAX_CHANNELS) {
41684 return MA_INVALID_ARGS;
41685 }
41686
41687 if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {
41688 return MA_INVALID_ARGS;
41689 }
41690 }
41691
41692 pDevice->pContext = pContext;
41693
41694 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
41695 pDevice->pUserData = pConfig->pUserData;
41696 pDevice->onData = pConfig->dataCallback;
41697 pDevice->onNotification = pConfig->notificationCallback;
41698 pDevice->onStop = pConfig->stopCallback;
41699
41700 if (pConfig->playback.pDeviceID != NULL) {
41701 MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
41702 pDevice->playback.pID = &pDevice->playback.id;
41703 } else {
41704 pDevice->playback.pID = NULL;
41705 }
41706
41707 if (pConfig->capture.pDeviceID != NULL) {
41708 MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
41709 pDevice->capture.pID = &pDevice->capture.id;
41710 } else {
41711 pDevice->capture.pID = NULL;
41712 }
41713
41714 pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer;
41715 pDevice->noClip = pConfig->noClip;
41716 pDevice->noDisableDenormals = pConfig->noDisableDenormals;
41717 pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback;
41718 ma_atomic_float_set(&pDevice->masterVolumeFactor, 1);
41719
41720 pDevice->type = pConfig->deviceType;
41721 pDevice->sampleRate = pConfig->sampleRate;
41722 pDevice->resampling.algorithm = pConfig->resampling.algorithm;
41723 pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
41724 pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable;
41725 pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData;
41726
41727 pDevice->capture.shareMode = pConfig->capture.shareMode;
41728 pDevice->capture.format = pConfig->capture.format;
41729 pDevice->capture.channels = pConfig->capture.channels;
41730 ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
41731 pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
41732 pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels;
41733
41734 pDevice->playback.shareMode = pConfig->playback.shareMode;
41735 pDevice->playback.format = pConfig->playback.format;
41736 pDevice->playback.channels = pConfig->playback.channels;
41737 ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
41738 pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
41739 pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels;
41740
41741 result = ma_mutex_init(&pDevice->startStopLock);
41742 if (result != MA_SUCCESS) {
41743 return result;
41744 }
41745
41746 /*
41747 When the device is started, the worker thread is the one that does the actual startup of the backend device. We
41748 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
41749
41750 Each of these semaphores is released internally by the worker thread when the work is completed. The start
41751 semaphore is also used to wake up the worker thread.
41752 */
41753 result = ma_event_init(&pDevice->wakeupEvent);
41754 if (result != MA_SUCCESS) {
41755 ma_mutex_uninit(&pDevice->startStopLock);
41756 return result;
41757 }
41758
41759 result = ma_event_init(&pDevice->startEvent);
41760 if (result != MA_SUCCESS) {
41761 ma_event_uninit(&pDevice->wakeupEvent);
41762 ma_mutex_uninit(&pDevice->startStopLock);
41763 return result;
41764 }
41765
41766 result = ma_event_init(&pDevice->stopEvent);
41767 if (result != MA_SUCCESS) {
41768 ma_event_uninit(&pDevice->startEvent);
41769 ma_event_uninit(&pDevice->wakeupEvent);
41770 ma_mutex_uninit(&pDevice->startStopLock);
41771 return result;
41772 }
41773
41774
41775 MA_ZERO_OBJECT(&descriptorPlayback);
41776 descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
41777 descriptorPlayback.shareMode = pConfig->playback.shareMode;
41778 descriptorPlayback.format = pConfig->playback.format;
41779 descriptorPlayback.channels = pConfig->playback.channels;
41780 descriptorPlayback.sampleRate = pConfig->sampleRate;
41781 ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
41782 descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
41783 descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
41784 descriptorPlayback.periodCount = pConfig->periods;
41785
41786 if (descriptorPlayback.periodCount == 0) {
41787 descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
41788 }
41789
41790
41791 MA_ZERO_OBJECT(&descriptorCapture);
41792 descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
41793 descriptorCapture.shareMode = pConfig->capture.shareMode;
41794 descriptorCapture.format = pConfig->capture.format;
41795 descriptorCapture.channels = pConfig->capture.channels;
41796 descriptorCapture.sampleRate = pConfig->sampleRate;
41797 ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
41798 descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
41799 descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
41800 descriptorCapture.periodCount = pConfig->periods;
41801
41802 if (descriptorCapture.periodCount == 0) {
41803 descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
41804 }
41805
41806
41807 result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
41808 if (result != MA_SUCCESS) {
41809 ma_event_uninit(&pDevice->startEvent);
41810 ma_event_uninit(&pDevice->wakeupEvent);
41811 ma_mutex_uninit(&pDevice->startStopLock);
41812 return result;
41813 }
41814
41815 #if 0
41816 /*
41817 On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
41818 the requested format and the internal format.
41819 */
41820 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41821 if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
41822 ma_device_uninit(pDevice);
41823 return MA_INVALID_ARGS;
41824 }
41825
41826 pDevice->capture.internalFormat = descriptorCapture.format;
41827 pDevice->capture.internalChannels = descriptorCapture.channels;
41828 pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
41829 ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
41830 pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
41831 pDevice->capture.internalPeriods = descriptorCapture.periodCount;
41832
41833 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
41834 pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);
41835 }
41836 }
41837
41838 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
41839 if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
41840 ma_device_uninit(pDevice);
41841 return MA_INVALID_ARGS;
41842 }
41843
41844 pDevice->playback.internalFormat = descriptorPlayback.format;
41845 pDevice->playback.internalChannels = descriptorPlayback.channels;
41846 pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
41847 ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
41848 pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
41849 pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
41850
41851 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
41852 pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);
41853 }
41854 }
41855
41856
41857 /*
41858 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.
41859 For loopback devices, we need to retrieve the name of the playback device.
41860 */
41861 {
41862 ma_device_info deviceInfo;
41863
41864 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41865 result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
41866 if (result == MA_SUCCESS) {
41867 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
41868 } else {
41869 /* We failed to retrieve the device info. Fall back to a default name. */
41870 if (descriptorCapture.pDeviceID == NULL) {
41871 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
41872 } else {
41873 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
41874 }
41875 }
41876 }
41877
41878 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
41879 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
41880 if (result == MA_SUCCESS) {
41881 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
41882 } else {
41883 /* We failed to retrieve the device info. Fall back to a default name. */
41884 if (descriptorPlayback.pDeviceID == NULL) {
41885 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
41886 } else {
41887 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
41888 }
41889 }
41890 }
41891 }
41892
41893
41894 ma_device__post_init_setup(pDevice, pConfig->deviceType);
41895 #endif
41896
41897 result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture);
41898 if (result != MA_SUCCESS) {
41899 ma_device_uninit(pDevice);
41900 return result;
41901 }
41902
41903
41904
41905 /*
41906 If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to
41907 be done after post_init_setup() because we'll need access to the sample rate.
41908 */
41909 if (pConfig->noFixedSizedCallback == MA_FALSE) {
41910 /* We're using a fixed sized data callback so we'll need an intermediary buffer. */
41911 ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames;
41912 if (intermediaryBufferCap == 0) {
41913 intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate);
41914 }
41915
41916 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
41917 ma_uint32 intermediaryBufferSizeInBytes;
41918
41919 pDevice->capture.intermediaryBufferLen = 0;
41920 pDevice->capture.intermediaryBufferCap = intermediaryBufferCap;
41921 if (pDevice->capture.intermediaryBufferCap == 0) {
41922 pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames;
41923 }
41924
41925 intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
41926
41927 pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
41928 if (pDevice->capture.pIntermediaryBuffer == NULL) {
41929 ma_device_uninit(pDevice);
41930 return MA_OUT_OF_MEMORY;
41931 }
41932
41933 /* Silence the buffer for safety. */
41934 ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels);
41935 pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap;
41936 }
41937
41938 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
41939 ma_uint64 intermediaryBufferSizeInBytes;
41940
41941 pDevice->playback.intermediaryBufferLen = 0;
41942 if (pConfig->deviceType == ma_device_type_duplex) {
41943 pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */
41944 } else {
41945 pDevice->playback.intermediaryBufferCap = intermediaryBufferCap;
41946 if (pDevice->playback.intermediaryBufferCap == 0) {
41947 pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames;
41948 }
41949 }
41950
41951 intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
41952
41953 pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
41954 if (pDevice->playback.pIntermediaryBuffer == NULL) {
41955 ma_device_uninit(pDevice);
41956 return MA_OUT_OF_MEMORY;
41957 }
41958
41959 /* Silence the buffer for safety. */
41960 ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels);
41961 pDevice->playback.intermediaryBufferLen = 0;
41962 }
41963 } else {
41964 /* Not using a fixed sized data callback so no need for an intermediary buffer. */
41965 }
41966
41967
41968 /* Some backends don't require the worker thread. */
41969 if (!ma_context_is_backend_asynchronous(pContext)) {
41970 /* The worker thread. */
41971 result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);
41972 if (result != MA_SUCCESS) {
41973 ma_device_uninit(pDevice);
41974 return result;
41975 }
41976
41977 /* Wait for the worker thread to put the device into it's stopped state for real. */
41978 ma_event_wait(&pDevice->stopEvent);
41979 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
41980 } else {
41981 /*
41982 If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done
41983 after ma_device__post_init_setup().
41984 */
41985 if (ma_context_is_backend_asynchronous(pContext)) {
41986 if (pConfig->deviceType == ma_device_type_duplex) {
41987 result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
41988 if (result != MA_SUCCESS) {
41989 ma_device_uninit(pDevice);
41990 return result;
41991 }
41992 }
41993 }
41994
41995 ma_device__set_state(pDevice, ma_device_state_stopped);
41996 }
41997
41998 /* Log device information. */
41999 {
42000 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
42001 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
42002 char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
42003 ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL);
42004
42005 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture");
42006 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));
42007 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels);
42008 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate);
42009 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));
42010 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
42011 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
42012 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
42013 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
42014 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
42015 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
42016 {
42017 char channelMapStr[1024];
42018 ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr));
42019 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
42020
42021 ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr));
42022 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
42023 }
42024 }
42025 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
42026 char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
42027 ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL);
42028
42029 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback");
42030 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));
42031 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
42032 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
42033 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));
42034 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
42035 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
42036 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
42037 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
42038 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
42039 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
42040 {
42041 char channelMapStr[1024];
42042 ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr));
42043 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
42044
42045 ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr));
42046 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
42047 }
42048 }
42049 }
42050
42051 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
42052 return MA_SUCCESS;
42053 }
42054
42055 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)
42056 {
42057 ma_result result;
42058 ma_context* pContext;
42059 ma_backend defaultBackends[ma_backend_null+1];
42060 ma_uint32 iBackend;
42061 ma_backend* pBackendsToIterate;
42062 ma_uint32 backendsToIterateCount;
42063 ma_allocation_callbacks allocationCallbacks;
42064
42065 if (pConfig == NULL) {
42066 return MA_INVALID_ARGS;
42067 }
42068
42069 if (pContextConfig != NULL) {
42070 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
42071 if (result != MA_SUCCESS) {
42072 return result;
42073 }
42074 } else {
42075 allocationCallbacks = ma_allocation_callbacks_init_default();
42076 }
42077
42078 pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks);
42079 if (pContext == NULL) {
42080 return MA_OUT_OF_MEMORY;
42081 }
42082
42083 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
42084 defaultBackends[iBackend] = (ma_backend)iBackend;
42085 }
42086
42087 pBackendsToIterate = (ma_backend*)backends;
42088 backendsToIterateCount = backendCount;
42089 if (pBackendsToIterate == NULL) {
42090 pBackendsToIterate = (ma_backend*)defaultBackends;
42091 backendsToIterateCount = ma_countof(defaultBackends);
42092 }
42093
42094 result = MA_NO_BACKEND;
42095
42096 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
42097 /*
42098 This is a hack for iOS. If the context config is null, there's a good chance the
42099 `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this
42100 case, set the session category based on the device type.
42101 */
42102 #if defined(MA_APPLE_MOBILE)
42103 ma_context_config contextConfig;
42104
42105 if (pContextConfig == NULL) {
42106 contextConfig = ma_context_config_init();
42107 switch (pConfig->deviceType) {
42108 case ma_device_type_duplex: {
42109 contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record;
42110 } break;
42111 case ma_device_type_capture: {
42112 contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record;
42113 } break;
42114 case ma_device_type_playback:
42115 default: {
42116 contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback;
42117 } break;
42118 }
42119
42120 pContextConfig = &contextConfig;
42121 }
42122 #endif
42123
42124 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
42125 if (result == MA_SUCCESS) {
42126 result = ma_device_init(pContext, pConfig, pDevice);
42127 if (result == MA_SUCCESS) {
42128 break; /* Success. */
42129 } else {
42130 ma_context_uninit(pContext); /* Failure. */
42131 }
42132 }
42133 }
42134
42135 if (result != MA_SUCCESS) {
42136 ma_free(pContext, &allocationCallbacks);
42137 return result;
42138 }
42139
42140 pDevice->isOwnerOfContext = MA_TRUE;
42141 return result;
42142 }
42143
42144 MA_API void ma_device_uninit(ma_device* pDevice)
42145 {
42146 if (!ma_device__is_initialized(pDevice)) {
42147 return;
42148 }
42149
42150 /* 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. */
42151 if (ma_device_is_started(pDevice)) {
42152 ma_device_stop(pDevice);
42153 }
42154
42155 /* Putting the device into an uninitialized state will make the worker thread return. */
42156 ma_device__set_state(pDevice, ma_device_state_uninitialized);
42157
42158 /* Wake up the worker thread and wait for it to properly terminate. */
42159 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
42160 ma_event_signal(&pDevice->wakeupEvent);
42161 ma_thread_wait(&pDevice->thread);
42162 }
42163
42164 if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
42165 pDevice->pContext->callbacks.onDeviceUninit(pDevice);
42166 }
42167
42168
42169 ma_event_uninit(&pDevice->stopEvent);
42170 ma_event_uninit(&pDevice->startEvent);
42171 ma_event_uninit(&pDevice->wakeupEvent);
42172 ma_mutex_uninit(&pDevice->startStopLock);
42173
42174 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
42175 if (pDevice->type == ma_device_type_duplex) {
42176 ma_duplex_rb_uninit(&pDevice->duplexRB);
42177 }
42178 }
42179
42180 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
42181 ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);
42182 }
42183 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
42184 ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);
42185 }
42186
42187 if (pDevice->playback.pInputCache != NULL) {
42188 ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
42189 }
42190
42191 if (pDevice->capture.pIntermediaryBuffer != NULL) {
42192 ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
42193 }
42194 if (pDevice->playback.pIntermediaryBuffer != NULL) {
42195 ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
42196 }
42197
42198 if (pDevice->isOwnerOfContext) {
42199 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
42200
42201 ma_context_uninit(pDevice->pContext);
42202 ma_free(pDevice->pContext, &allocationCallbacks);
42203 }
42204
42205 MA_ZERO_OBJECT(pDevice);
42206 }
42207
42208 MA_API ma_context* ma_device_get_context(ma_device* pDevice)
42209 {
42210 if (pDevice == NULL) {
42211 return NULL;
42212 }
42213
42214 return pDevice->pContext;
42215 }
42216
42217 MA_API ma_log* ma_device_get_log(ma_device* pDevice)
42218 {
42219 return ma_context_get_log(ma_device_get_context(pDevice));
42220 }
42221
42222 MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
42223 {
42224 if (pDeviceInfo == NULL) {
42225 return MA_INVALID_ARGS;
42226 }
42227
42228 MA_ZERO_OBJECT(pDeviceInfo);
42229
42230 if (pDevice == NULL) {
42231 return MA_INVALID_ARGS;
42232 }
42233
42234 /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */
42235 if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) {
42236 return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo);
42237 }
42238
42239 /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */
42240 if (type == ma_device_type_playback) {
42241 return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo);
42242 } else {
42243 return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo);
42244 }
42245 }
42246
42247 MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator)
42248 {
42249 ma_result result;
42250 ma_device_info deviceInfo;
42251
42252 if (pLengthNotIncludingNullTerminator != NULL) {
42253 *pLengthNotIncludingNullTerminator = 0;
42254 }
42255
42256 if (pName != NULL && nameCap > 0) {
42257 pName[0] = '\0';
42258 }
42259
42260 result = ma_device_get_info(pDevice, type, &deviceInfo);
42261 if (result != MA_SUCCESS) {
42262 return result;
42263 }
42264
42265 if (pName != NULL) {
42266 ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1);
42267
42268 /*
42269 For safety, make sure the length is based on the truncated output string rather than the
42270 source. Otherwise the caller might assume the output buffer contains more content than it
42271 actually does.
42272 */
42273 if (pLengthNotIncludingNullTerminator != NULL) {
42274 *pLengthNotIncludingNullTerminator = strlen(pName);
42275 }
42276 } else {
42277 /* Name not specified. Just report the length of the source string. */
42278 if (pLengthNotIncludingNullTerminator != NULL) {
42279 *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name);
42280 }
42281 }
42282
42283 return MA_SUCCESS;
42284 }
42285
42286 MA_API ma_result ma_device_start(ma_device* pDevice)
42287 {
42288 ma_result result;
42289
42290 if (pDevice == NULL) {
42291 return MA_INVALID_ARGS;
42292 }
42293
42294 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
42295 return MA_INVALID_OPERATION; /* Not initialized. */
42296 }
42297
42298 if (ma_device_get_state(pDevice) == ma_device_state_started) {
42299 return MA_SUCCESS; /* Already started. */
42300 }
42301
42302 ma_mutex_lock(&pDevice->startStopLock);
42303 {
42304 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
42305 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
42306
42307 ma_device__set_state(pDevice, ma_device_state_starting);
42308
42309 /* Asynchronous backends need to be handled differently. */
42310 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
42311 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
42312 result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
42313 } else {
42314 result = MA_INVALID_OPERATION;
42315 }
42316
42317 if (result == MA_SUCCESS) {
42318 ma_device__set_state(pDevice, ma_device_state_started);
42319 ma_device__on_notification_started(pDevice);
42320 }
42321 } else {
42322 /*
42323 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
42324 thread and then wait for the start event.
42325 */
42326 ma_event_signal(&pDevice->wakeupEvent);
42327
42328 /*
42329 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
42330 into the started state. Don't call ma_device__set_state() here.
42331 */
42332 ma_event_wait(&pDevice->startEvent);
42333 result = pDevice->workResult;
42334 }
42335
42336 /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
42337 if (result != MA_SUCCESS) {
42338 ma_device__set_state(pDevice, ma_device_state_stopped);
42339 }
42340 }
42341 ma_mutex_unlock(&pDevice->startStopLock);
42342
42343 return result;
42344 }
42345
42346 MA_API ma_result ma_device_stop(ma_device* pDevice)
42347 {
42348 ma_result result;
42349
42350 if (pDevice == NULL) {
42351 return MA_INVALID_ARGS;
42352 }
42353
42354 if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
42355 return MA_INVALID_OPERATION; /* Not initialized. */
42356 }
42357
42358 if (ma_device_get_state(pDevice) == ma_device_state_stopped) {
42359 return MA_SUCCESS; /* Already stopped. */
42360 }
42361
42362 ma_mutex_lock(&pDevice->startStopLock);
42363 {
42364 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
42365 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
42366
42367 ma_device__set_state(pDevice, ma_device_state_stopping);
42368
42369 /* Asynchronous backends need to be handled differently. */
42370 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
42371 /* Asynchronous backends must have a stop operation. */
42372 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
42373 result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
42374 } else {
42375 result = MA_INVALID_OPERATION;
42376 }
42377
42378 ma_device__set_state(pDevice, ma_device_state_stopped);
42379 } else {
42380 /*
42381 Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
42382 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
42383 sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
42384 important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
42385 */
42386 MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
42387
42388 if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
42389 pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
42390 }
42391
42392 /*
42393 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
42394 the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
42395 */
42396 ma_event_wait(&pDevice->stopEvent);
42397 result = MA_SUCCESS;
42398 }
42399
42400 /*
42401 This is a safety measure to ensure the internal buffer has been cleared so any leftover
42402 does not get played the next time the device starts. Ideally this should be drained by
42403 the backend first.
42404 */
42405 pDevice->playback.intermediaryBufferLen = 0;
42406 pDevice->playback.inputCacheConsumed = 0;
42407 pDevice->playback.inputCacheRemaining = 0;
42408 }
42409 ma_mutex_unlock(&pDevice->startStopLock);
42410
42411 return result;
42412 }
42413
42414 MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice)
42415 {
42416 return ma_device_get_state(pDevice) == ma_device_state_started;
42417 }
42418
42419 MA_API ma_device_state ma_device_get_state(const ma_device* pDevice)
42420 {
42421 if (pDevice == NULL) {
42422 return ma_device_state_uninitialized;
42423 }
42424
42425 return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */
42426 }
42427
42428 MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
42429 {
42430 if (pDevice == NULL) {
42431 return MA_INVALID_ARGS;
42432 }
42433
42434 if (volume < 0.0f) {
42435 return MA_INVALID_ARGS;
42436 }
42437
42438 ma_atomic_float_set(&pDevice->masterVolumeFactor, volume);
42439
42440 return MA_SUCCESS;
42441 }
42442
42443 MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
42444 {
42445 if (pVolume == NULL) {
42446 return MA_INVALID_ARGS;
42447 }
42448
42449 if (pDevice == NULL) {
42450 *pVolume = 0;
42451 return MA_INVALID_ARGS;
42452 }
42453
42454 *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor);
42455
42456 return MA_SUCCESS;
42457 }
42458
42459 MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB)
42460 {
42461 if (gainDB > 0) {
42462 return MA_INVALID_ARGS;
42463 }
42464
42465 return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB));
42466 }
42467
42468 MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB)
42469 {
42470 float factor;
42471 ma_result result;
42472
42473 if (pGainDB == NULL) {
42474 return MA_INVALID_ARGS;
42475 }
42476
42477 result = ma_device_get_master_volume(pDevice, &factor);
42478 if (result != MA_SUCCESS) {
42479 *pGainDB = 0;
42480 return result;
42481 }
42482
42483 *pGainDB = ma_volume_linear_to_db(factor);
42484
42485 return MA_SUCCESS;
42486 }
42487
42488
42489 MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
42490 {
42491 if (pDevice == NULL) {
42492 return MA_INVALID_ARGS;
42493 }
42494
42495 if (pOutput == NULL && pInput == NULL) {
42496 return MA_INVALID_ARGS;
42497 }
42498
42499 if (pDevice->type == ma_device_type_duplex) {
42500 if (pInput != NULL) {
42501 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
42502 }
42503
42504 if (pOutput != NULL) {
42505 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
42506 }
42507 } else {
42508 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
42509 if (pInput == NULL) {
42510 return MA_INVALID_ARGS;
42511 }
42512
42513 ma_device__send_frames_to_client(pDevice, frameCount, pInput);
42514 }
42515
42516 if (pDevice->type == ma_device_type_playback) {
42517 if (pOutput == NULL) {
42518 return MA_INVALID_ARGS;
42519 }
42520
42521 ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
42522 }
42523 }
42524
42525 return MA_SUCCESS;
42526 }
42527
42528 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
42529 {
42530 if (pDescriptor == NULL) {
42531 return 0;
42532 }
42533
42534 /*
42535 We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the
42536 time when the size of the buffer needs to be determined. In this case we need to just take a best
42537 guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll
42538 just fall back to MA_DEFAULT_SAMPLE_RATE.
42539 */
42540 if (nativeSampleRate == 0) {
42541 nativeSampleRate = pDescriptor->sampleRate;
42542 }
42543 if (nativeSampleRate == 0) {
42544 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
42545 }
42546
42547 MA_ASSERT(nativeSampleRate != 0);
42548
42549 if (pDescriptor->periodSizeInFrames == 0) {
42550 if (pDescriptor->periodSizeInMilliseconds == 0) {
42551 if (performanceProfile == ma_performance_profile_low_latency) {
42552 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
42553 } else {
42554 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
42555 }
42556 } else {
42557 return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
42558 }
42559 } else {
42560 return pDescriptor->periodSizeInFrames;
42561 }
42562 }
42563 #endif /* MA_NO_DEVICE_IO */
42564
42565
42566 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
42567 {
42568 /* Prevent a division by zero. */
42569 if (sampleRate == 0) {
42570 return 0;
42571 }
42572
42573 return bufferSizeInFrames*1000 / sampleRate;
42574 }
42575
42576 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
42577 {
42578 /* Prevent a division by zero. */
42579 if (sampleRate == 0) {
42580 return 0;
42581 }
42582
42583 return bufferSizeInMilliseconds*sampleRate / 1000;
42584 }
42585
42586 MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
42587 {
42588 if (dst == src) {
42589 return; /* No-op. */
42590 }
42591
42592 ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
42593 }
42594
42595 MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
42596 {
42597 if (format == ma_format_u8) {
42598 ma_uint64 sampleCount = frameCount * channels;
42599 ma_uint64 iSample;
42600 for (iSample = 0; iSample < sampleCount; iSample += 1) {
42601 ((ma_uint8*)p)[iSample] = 128;
42602 }
42603 } else {
42604 ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));
42605 }
42606 }
42607
42608 MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
42609 {
42610 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
42611 }
42612
42613 MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
42614 {
42615 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
42616 }
42617
42618
42619 MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)
42620 {
42621 ma_uint64 iSample;
42622
42623 MA_ASSERT(pDst != NULL);
42624 MA_ASSERT(pSrc != NULL);
42625
42626 for (iSample = 0; iSample < count; iSample += 1) {
42627 pDst[iSample] = ma_clip_u8(pSrc[iSample]);
42628 }
42629 }
42630
42631 MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count)
42632 {
42633 ma_uint64 iSample;
42634
42635 MA_ASSERT(pDst != NULL);
42636 MA_ASSERT(pSrc != NULL);
42637
42638 for (iSample = 0; iSample < count; iSample += 1) {
42639 pDst[iSample] = ma_clip_s16(pSrc[iSample]);
42640 }
42641 }
42642
42643 MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count)
42644 {
42645 ma_uint64 iSample;
42646
42647 MA_ASSERT(pDst != NULL);
42648 MA_ASSERT(pSrc != NULL);
42649
42650 for (iSample = 0; iSample < count; iSample += 1) {
42651 ma_int64 s = ma_clip_s24(pSrc[iSample]);
42652 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
42653 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
42654 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
42655 }
42656 }
42657
42658 MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)
42659 {
42660 ma_uint64 iSample;
42661
42662 MA_ASSERT(pDst != NULL);
42663 MA_ASSERT(pSrc != NULL);
42664
42665 for (iSample = 0; iSample < count; iSample += 1) {
42666 pDst[iSample] = ma_clip_s32(pSrc[iSample]);
42667 }
42668 }
42669
42670 MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count)
42671 {
42672 ma_uint64 iSample;
42673
42674 MA_ASSERT(pDst != NULL);
42675 MA_ASSERT(pSrc != NULL);
42676
42677 for (iSample = 0; iSample < count; iSample += 1) {
42678 pDst[iSample] = ma_clip_f32(pSrc[iSample]);
42679 }
42680 }
42681
42682 MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
42683 {
42684 ma_uint64 sampleCount;
42685
42686 MA_ASSERT(pDst != NULL);
42687 MA_ASSERT(pSrc != NULL);
42688
42689 sampleCount = frameCount * channels;
42690
42691 switch (format) {
42692 case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break;
42693 case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break;
42694 case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break;
42695 case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break;
42696 case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break;
42697
42698 /* 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. */
42699 case ma_format_unknown:
42700 case ma_format_count:
42701 break;
42702 }
42703 }
42704
42705
42706 MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
42707 {
42708 ma_uint64 iSample;
42709
42710 if (pSamplesOut == NULL || pSamplesIn == NULL) {
42711 return;
42712 }
42713
42714 for (iSample = 0; iSample < sampleCount; iSample += 1) {
42715 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
42716 }
42717 }
42718
42719 MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
42720 {
42721 ma_uint64 iSample;
42722
42723 if (pSamplesOut == NULL || pSamplesIn == NULL) {
42724 return;
42725 }
42726
42727 for (iSample = 0; iSample < sampleCount; iSample += 1) {
42728 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
42729 }
42730 }
42731
42732 MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
42733 {
42734 ma_uint64 iSample;
42735 ma_uint8* pSamplesOut8;
42736 ma_uint8* pSamplesIn8;
42737
42738 if (pSamplesOut == NULL || pSamplesIn == NULL) {
42739 return;
42740 }
42741
42742 pSamplesOut8 = (ma_uint8*)pSamplesOut;
42743 pSamplesIn8 = (ma_uint8*)pSamplesIn;
42744
42745 for (iSample = 0; iSample < sampleCount; iSample += 1) {
42746 ma_int32 sampleS32;
42747
42748 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
42749 sampleS32 = (ma_int32)(sampleS32 * factor);
42750
42751 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
42752 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
42753 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
42754 }
42755 }
42756
42757 MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
42758 {
42759 ma_uint64 iSample;
42760
42761 if (pSamplesOut == NULL || pSamplesIn == NULL) {
42762 return;
42763 }
42764
42765 for (iSample = 0; iSample < sampleCount; iSample += 1) {
42766 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
42767 }
42768 }
42769
42770 MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
42771 {
42772 ma_uint64 iSample;
42773
42774 if (pSamplesOut == NULL || pSamplesIn == NULL) {
42775 return;
42776 }
42777
42778 if (factor == 1) {
42779 if (pSamplesOut == pSamplesIn) {
42780 /* In place. No-op. */
42781 } else {
42782 /* Just a copy. */
42783 for (iSample = 0; iSample < sampleCount; iSample += 1) {
42784 pSamplesOut[iSample] = pSamplesIn[iSample];
42785 }
42786 }
42787 } else {
42788 for (iSample = 0; iSample < sampleCount; iSample += 1) {
42789 pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
42790 }
42791 }
42792 }
42793
42794 MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
42795 {
42796 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
42797 }
42798
42799 MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
42800 {
42801 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
42802 }
42803
42804 MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
42805 {
42806 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
42807 }
42808
42809 MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
42810 {
42811 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
42812 }
42813
42814 MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
42815 {
42816 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
42817 }
42818
42819 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)
42820 {
42821 ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor);
42822 }
42823
42824 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)
42825 {
42826 ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor);
42827 }
42828
42829 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)
42830 {
42831 ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor);
42832 }
42833
42834 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)
42835 {
42836 ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor);
42837 }
42838
42839 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)
42840 {
42841 ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor);
42842 }
42843
42844 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)
42845 {
42846 switch (format)
42847 {
42848 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return;
42849 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return;
42850 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return;
42851 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return;
42852 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return;
42853 default: return; /* Do nothing. */
42854 }
42855 }
42856
42857 MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42858 {
42859 ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor);
42860 }
42861
42862 MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42863 {
42864 ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor);
42865 }
42866
42867 MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42868 {
42869 ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor);
42870 }
42871
42872 MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42873 {
42874 ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor);
42875 }
42876
42877 MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
42878 {
42879 ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor);
42880 }
42881
42882 MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
42883 {
42884 ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor);
42885 }
42886
42887
42888 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)
42889 {
42890 ma_uint64 iFrame;
42891
42892 if (channels == 2) {
42893 /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */
42894 }
42895
42896 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
42897 ma_uint32 iChannel;
42898 for (iChannel = 0; iChannel < channels; iChannel += 1) {
42899 pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel];
42900 }
42901 }
42902 }
42903
42904
42905
42906 static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume)
42907 {
42908 return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8);
42909 }
42910
42911 static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume)
42912 {
42913 return (ma_int32)((x * volume) >> 8);
42914 }
42915
42916 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume)
42917 {
42918 return (ma_int64)((x * volume) >> 8);
42919 }
42920
42921 static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume)
42922 {
42923 return (ma_int64)((x * volume) >> 8);
42924 }
42925
42926 static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume)
42927 {
42928 return x * volume;
42929 }
42930
42931
42932 MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)
42933 {
42934 ma_uint64 iSample;
42935 ma_int16 volumeFixed;
42936
42937 MA_ASSERT(pDst != NULL);
42938 MA_ASSERT(pSrc != NULL);
42939
42940 volumeFixed = ma_float_to_fixed_16(volume);
42941
42942 for (iSample = 0; iSample < count; iSample += 1) {
42943 pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));
42944 }
42945 }
42946
42947 MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)
42948 {
42949 ma_uint64 iSample;
42950 ma_int16 volumeFixed;
42951
42952 MA_ASSERT(pDst != NULL);
42953 MA_ASSERT(pSrc != NULL);
42954
42955 volumeFixed = ma_float_to_fixed_16(volume);
42956
42957 for (iSample = 0; iSample < count; iSample += 1) {
42958 pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));
42959 }
42960 }
42961
42962 MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
42963 {
42964 ma_uint64 iSample;
42965 ma_int16 volumeFixed;
42966
42967 MA_ASSERT(pDst != NULL);
42968 MA_ASSERT(pSrc != NULL);
42969
42970 volumeFixed = ma_float_to_fixed_16(volume);
42971
42972 for (iSample = 0; iSample < count; iSample += 1) {
42973 ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));
42974 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
42975 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
42976 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
42977 }
42978 }
42979
42980 MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
42981 {
42982 ma_uint64 iSample;
42983 ma_int16 volumeFixed;
42984
42985 MA_ASSERT(pDst != NULL);
42986 MA_ASSERT(pSrc != NULL);
42987
42988 volumeFixed = ma_float_to_fixed_16(volume);
42989
42990 for (iSample = 0; iSample < count; iSample += 1) {
42991 pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed));
42992 }
42993 }
42994
42995 MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)
42996 {
42997 ma_uint64 iSample;
42998
42999 MA_ASSERT(pDst != NULL);
43000 MA_ASSERT(pSrc != NULL);
43001
43002 /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */
43003
43004 for (iSample = 0; iSample < count; iSample += 1) {
43005 pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume));
43006 }
43007 }
43008
43009 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)
43010 {
43011 MA_ASSERT(pDst != NULL);
43012 MA_ASSERT(pSrc != NULL);
43013
43014 if (volume == 1) {
43015 ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */
43016 } else if (volume == 0) {
43017 ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */
43018 } else {
43019 ma_uint64 sampleCount = frameCount * channels;
43020
43021 switch (format) {
43022 case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;
43023 case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;
43024 case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
43025 case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
43026 case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break;
43027
43028 /* 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. */
43029 case ma_format_unknown:
43030 case ma_format_count:
43031 break;
43032 }
43033 }
43034 }
43035
43036
43037
43038 MA_API float ma_volume_linear_to_db(float factor)
43039 {
43040 return 20*ma_log10f(factor);
43041 }
43042
43043 MA_API float ma_volume_db_to_linear(float gain)
43044 {
43045 return ma_powf(10, gain/20.0f);
43046 }
43047
43048
43049 MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
43050 {
43051 ma_uint64 iSample;
43052 ma_uint64 sampleCount;
43053
43054 if (pDst == NULL || pSrc == NULL || channels == 0) {
43055 return MA_INVALID_ARGS;
43056 }
43057
43058 if (volume == 0) {
43059 return MA_SUCCESS; /* No changes if the volume is 0. */
43060 }
43061
43062 sampleCount = frameCount * channels;
43063
43064 if (volume == 1) {
43065 for (iSample = 0; iSample < sampleCount; iSample += 1) {
43066 pDst[iSample] += pSrc[iSample];
43067 }
43068 } else {
43069 for (iSample = 0; iSample < sampleCount; iSample += 1) {
43070 pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
43071 }
43072 }
43073
43074 return MA_SUCCESS;
43075 }
43076
43077
43078
43079 /**************************************************************************************************************************************************************
43080
43081 Format Conversion
43082
43083 **************************************************************************************************************************************************************/
43084
43085 static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
43086 {
43087 return (ma_int16)(x * 32767.0f);
43088 }
43089
43090 static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
43091 {
43092 return (ma_int16)((ma_int16)x - 128);
43093 }
43094
43095 static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)
43096 {
43097 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. */
43098 }
43099
43100 static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)
43101 {
43102 s24[0] = (ma_uint8)((x & 0x000000FF) >> 0);
43103 s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8);
43104 s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);
43105 }
43106
43107
43108 /* u8 */
43109 MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43110 {
43111 (void)ditherMode;
43112 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
43113 }
43114
43115
43116 static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43117 {
43118 ma_int16* dst_s16 = (ma_int16*)dst;
43119 const ma_uint8* src_u8 = (const ma_uint8*)src;
43120
43121 ma_uint64 i;
43122 for (i = 0; i < count; i += 1) {
43123 ma_int16 x = src_u8[i];
43124 x = (ma_int16)(x - 128);
43125 x = (ma_int16)(x << 8);
43126 dst_s16[i] = x;
43127 }
43128
43129 (void)ditherMode;
43130 }
43131
43132 static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43133 {
43134 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
43135 }
43136
43137 #if defined(MA_SUPPORT_SSE2)
43138 static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43139 {
43140 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
43141 }
43142 #endif
43143 #if defined(MA_SUPPORT_NEON)
43144 static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43145 {
43146 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
43147 }
43148 #endif
43149
43150 MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43151 {
43152 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43153 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
43154 #else
43155 # if defined(MA_SUPPORT_SSE2)
43156 if (ma_has_sse2()) {
43157 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
43158 } else
43159 #elif defined(MA_SUPPORT_NEON)
43160 if (ma_has_neon()) {
43161 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
43162 } else
43163 #endif
43164 {
43165 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
43166 }
43167 #endif
43168 }
43169
43170
43171 static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43172 {
43173 ma_uint8* dst_s24 = (ma_uint8*)dst;
43174 const ma_uint8* src_u8 = (const ma_uint8*)src;
43175
43176 ma_uint64 i;
43177 for (i = 0; i < count; i += 1) {
43178 ma_int16 x = src_u8[i];
43179 x = (ma_int16)(x - 128);
43180
43181 dst_s24[i*3+0] = 0;
43182 dst_s24[i*3+1] = 0;
43183 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
43184 }
43185
43186 (void)ditherMode;
43187 }
43188
43189 static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43190 {
43191 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
43192 }
43193
43194 #if defined(MA_SUPPORT_SSE2)
43195 static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43196 {
43197 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
43198 }
43199 #endif
43200 #if defined(MA_SUPPORT_NEON)
43201 static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43202 {
43203 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
43204 }
43205 #endif
43206
43207 MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43208 {
43209 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43210 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
43211 #else
43212 # if defined(MA_SUPPORT_SSE2)
43213 if (ma_has_sse2()) {
43214 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
43215 } else
43216 #elif defined(MA_SUPPORT_NEON)
43217 if (ma_has_neon()) {
43218 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
43219 } else
43220 #endif
43221 {
43222 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
43223 }
43224 #endif
43225 }
43226
43227
43228 static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43229 {
43230 ma_int32* dst_s32 = (ma_int32*)dst;
43231 const ma_uint8* src_u8 = (const ma_uint8*)src;
43232
43233 ma_uint64 i;
43234 for (i = 0; i < count; i += 1) {
43235 ma_int32 x = src_u8[i];
43236 x = x - 128;
43237 x = x << 24;
43238 dst_s32[i] = x;
43239 }
43240
43241 (void)ditherMode;
43242 }
43243
43244 static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43245 {
43246 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
43247 }
43248
43249 #if defined(MA_SUPPORT_SSE2)
43250 static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43251 {
43252 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
43253 }
43254 #endif
43255 #if defined(MA_SUPPORT_NEON)
43256 static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43257 {
43258 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
43259 }
43260 #endif
43261
43262 MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43263 {
43264 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43265 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
43266 #else
43267 # if defined(MA_SUPPORT_SSE2)
43268 if (ma_has_sse2()) {
43269 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
43270 } else
43271 #elif defined(MA_SUPPORT_NEON)
43272 if (ma_has_neon()) {
43273 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
43274 } else
43275 #endif
43276 {
43277 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
43278 }
43279 #endif
43280 }
43281
43282
43283 static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43284 {
43285 float* dst_f32 = (float*)dst;
43286 const ma_uint8* src_u8 = (const ma_uint8*)src;
43287
43288 ma_uint64 i;
43289 for (i = 0; i < count; i += 1) {
43290 float x = (float)src_u8[i];
43291 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
43292 x = x - 1; /* 0..2 to -1..1 */
43293
43294 dst_f32[i] = x;
43295 }
43296
43297 (void)ditherMode;
43298 }
43299
43300 static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43301 {
43302 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
43303 }
43304
43305 #if defined(MA_SUPPORT_SSE2)
43306 static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43307 {
43308 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
43309 }
43310 #endif
43311 #if defined(MA_SUPPORT_NEON)
43312 static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43313 {
43314 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
43315 }
43316 #endif
43317
43318 MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43319 {
43320 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43321 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
43322 #else
43323 # if defined(MA_SUPPORT_SSE2)
43324 if (ma_has_sse2()) {
43325 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
43326 } else
43327 #elif defined(MA_SUPPORT_NEON)
43328 if (ma_has_neon()) {
43329 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
43330 } else
43331 #endif
43332 {
43333 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
43334 }
43335 #endif
43336 }
43337
43338
43339 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43340 static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43341 {
43342 ma_uint8* dst_u8 = (ma_uint8*)dst;
43343 const ma_uint8** src_u8 = (const ma_uint8**)src;
43344
43345 ma_uint64 iFrame;
43346 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43347 ma_uint32 iChannel;
43348 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43349 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
43350 }
43351 }
43352 }
43353 #else
43354 static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43355 {
43356 ma_uint8* dst_u8 = (ma_uint8*)dst;
43357 const ma_uint8** src_u8 = (const ma_uint8**)src;
43358
43359 if (channels == 1) {
43360 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
43361 } else if (channels == 2) {
43362 ma_uint64 iFrame;
43363 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43364 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
43365 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
43366 }
43367 } else {
43368 ma_uint64 iFrame;
43369 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43370 ma_uint32 iChannel;
43371 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43372 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
43373 }
43374 }
43375 }
43376 }
43377 #endif
43378
43379 MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43380 {
43381 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43382 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
43383 #else
43384 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
43385 #endif
43386 }
43387
43388
43389 static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43390 {
43391 ma_uint8** dst_u8 = (ma_uint8**)dst;
43392 const ma_uint8* src_u8 = (const ma_uint8*)src;
43393
43394 ma_uint64 iFrame;
43395 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43396 ma_uint32 iChannel;
43397 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43398 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
43399 }
43400 }
43401 }
43402
43403 static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43404 {
43405 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
43406 }
43407
43408 MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43409 {
43410 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43411 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
43412 #else
43413 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
43414 #endif
43415 }
43416
43417
43418 /* s16 */
43419 static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43420 {
43421 ma_uint8* dst_u8 = (ma_uint8*)dst;
43422 const ma_int16* src_s16 = (const ma_int16*)src;
43423
43424 if (ditherMode == ma_dither_mode_none) {
43425 ma_uint64 i;
43426 for (i = 0; i < count; i += 1) {
43427 ma_int16 x = src_s16[i];
43428 x = (ma_int16)(x >> 8);
43429 x = (ma_int16)(x + 128);
43430 dst_u8[i] = (ma_uint8)x;
43431 }
43432 } else {
43433 ma_uint64 i;
43434 for (i = 0; i < count; i += 1) {
43435 ma_int16 x = src_s16[i];
43436
43437 /* Dither. Don't overflow. */
43438 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
43439 if ((x + dither) <= 0x7FFF) {
43440 x = (ma_int16)(x + dither);
43441 } else {
43442 x = 0x7FFF;
43443 }
43444
43445 x = (ma_int16)(x >> 8);
43446 x = (ma_int16)(x + 128);
43447 dst_u8[i] = (ma_uint8)x;
43448 }
43449 }
43450 }
43451
43452 static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43453 {
43454 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
43455 }
43456
43457 #if defined(MA_SUPPORT_SSE2)
43458 static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43459 {
43460 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
43461 }
43462 #endif
43463 #if defined(MA_SUPPORT_NEON)
43464 static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43465 {
43466 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
43467 }
43468 #endif
43469
43470 MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43471 {
43472 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43473 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
43474 #else
43475 # if defined(MA_SUPPORT_SSE2)
43476 if (ma_has_sse2()) {
43477 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
43478 } else
43479 #elif defined(MA_SUPPORT_NEON)
43480 if (ma_has_neon()) {
43481 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
43482 } else
43483 #endif
43484 {
43485 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
43486 }
43487 #endif
43488 }
43489
43490
43491 MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43492 {
43493 (void)ditherMode;
43494 ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
43495 }
43496
43497
43498 static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43499 {
43500 ma_uint8* dst_s24 = (ma_uint8*)dst;
43501 const ma_int16* src_s16 = (const ma_int16*)src;
43502
43503 ma_uint64 i;
43504 for (i = 0; i < count; i += 1) {
43505 dst_s24[i*3+0] = 0;
43506 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
43507 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
43508 }
43509
43510 (void)ditherMode;
43511 }
43512
43513 static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43514 {
43515 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
43516 }
43517
43518 #if defined(MA_SUPPORT_SSE2)
43519 static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43520 {
43521 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
43522 }
43523 #endif
43524 #if defined(MA_SUPPORT_NEON)
43525 static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43526 {
43527 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
43528 }
43529 #endif
43530
43531 MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43532 {
43533 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43534 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
43535 #else
43536 # if defined(MA_SUPPORT_SSE2)
43537 if (ma_has_sse2()) {
43538 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
43539 } else
43540 #elif defined(MA_SUPPORT_NEON)
43541 if (ma_has_neon()) {
43542 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
43543 } else
43544 #endif
43545 {
43546 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
43547 }
43548 #endif
43549 }
43550
43551
43552 static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43553 {
43554 ma_int32* dst_s32 = (ma_int32*)dst;
43555 const ma_int16* src_s16 = (const ma_int16*)src;
43556
43557 ma_uint64 i;
43558 for (i = 0; i < count; i += 1) {
43559 dst_s32[i] = src_s16[i] << 16;
43560 }
43561
43562 (void)ditherMode;
43563 }
43564
43565 static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43566 {
43567 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
43568 }
43569
43570 #if defined(MA_SUPPORT_SSE2)
43571 static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43572 {
43573 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
43574 }
43575 #endif
43576 #if defined(MA_SUPPORT_NEON)
43577 static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43578 {
43579 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
43580 }
43581 #endif
43582
43583 MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43584 {
43585 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43586 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
43587 #else
43588 # if defined(MA_SUPPORT_SSE2)
43589 if (ma_has_sse2()) {
43590 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
43591 } else
43592 #elif defined(MA_SUPPORT_NEON)
43593 if (ma_has_neon()) {
43594 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
43595 } else
43596 #endif
43597 {
43598 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
43599 }
43600 #endif
43601 }
43602
43603
43604 static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43605 {
43606 float* dst_f32 = (float*)dst;
43607 const ma_int16* src_s16 = (const ma_int16*)src;
43608
43609 ma_uint64 i;
43610 for (i = 0; i < count; i += 1) {
43611 float x = (float)src_s16[i];
43612
43613 #if 0
43614 /* The accurate way. */
43615 x = x + 32768.0f; /* -32768..32767 to 0..65535 */
43616 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
43617 x = x - 1; /* 0..2 to -1..1 */
43618 #else
43619 /* The fast way. */
43620 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
43621 #endif
43622
43623 dst_f32[i] = x;
43624 }
43625
43626 (void)ditherMode;
43627 }
43628
43629 static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43630 {
43631 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
43632 }
43633
43634 #if defined(MA_SUPPORT_SSE2)
43635 static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43636 {
43637 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
43638 }
43639 #endif
43640 #if defined(MA_SUPPORT_NEON)
43641 static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43642 {
43643 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
43644 }
43645 #endif
43646
43647 MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43648 {
43649 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43650 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
43651 #else
43652 # if defined(MA_SUPPORT_SSE2)
43653 if (ma_has_sse2()) {
43654 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
43655 } else
43656 #elif defined(MA_SUPPORT_NEON)
43657 if (ma_has_neon()) {
43658 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
43659 } else
43660 #endif
43661 {
43662 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
43663 }
43664 #endif
43665 }
43666
43667
43668 static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43669 {
43670 ma_int16* dst_s16 = (ma_int16*)dst;
43671 const ma_int16** src_s16 = (const ma_int16**)src;
43672
43673 ma_uint64 iFrame;
43674 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43675 ma_uint32 iChannel;
43676 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43677 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
43678 }
43679 }
43680 }
43681
43682 static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43683 {
43684 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
43685 }
43686
43687 MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43688 {
43689 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43690 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
43691 #else
43692 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
43693 #endif
43694 }
43695
43696
43697 static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43698 {
43699 ma_int16** dst_s16 = (ma_int16**)dst;
43700 const ma_int16* src_s16 = (const ma_int16*)src;
43701
43702 ma_uint64 iFrame;
43703 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43704 ma_uint32 iChannel;
43705 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43706 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
43707 }
43708 }
43709 }
43710
43711 static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43712 {
43713 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
43714 }
43715
43716 MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
43717 {
43718 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43719 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
43720 #else
43721 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
43722 #endif
43723 }
43724
43725
43726 /* s24 */
43727 static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43728 {
43729 ma_uint8* dst_u8 = (ma_uint8*)dst;
43730 const ma_uint8* src_s24 = (const ma_uint8*)src;
43731
43732 if (ditherMode == ma_dither_mode_none) {
43733 ma_uint64 i;
43734 for (i = 0; i < count; i += 1) {
43735 dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);
43736 }
43737 } else {
43738 ma_uint64 i;
43739 for (i = 0; i < count; i += 1) {
43740 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);
43741
43742 /* Dither. Don't overflow. */
43743 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
43744 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
43745 x = x + dither;
43746 } else {
43747 x = 0x7FFFFFFF;
43748 }
43749
43750 x = x >> 24;
43751 x = x + 128;
43752 dst_u8[i] = (ma_uint8)x;
43753 }
43754 }
43755 }
43756
43757 static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43758 {
43759 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
43760 }
43761
43762 #if defined(MA_SUPPORT_SSE2)
43763 static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43764 {
43765 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
43766 }
43767 #endif
43768 #if defined(MA_SUPPORT_NEON)
43769 static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43770 {
43771 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
43772 }
43773 #endif
43774
43775 MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43776 {
43777 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43778 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
43779 #else
43780 # if defined(MA_SUPPORT_SSE2)
43781 if (ma_has_sse2()) {
43782 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
43783 } else
43784 #elif defined(MA_SUPPORT_NEON)
43785 if (ma_has_neon()) {
43786 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
43787 } else
43788 #endif
43789 {
43790 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
43791 }
43792 #endif
43793 }
43794
43795
43796 static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43797 {
43798 ma_int16* dst_s16 = (ma_int16*)dst;
43799 const ma_uint8* src_s24 = (const ma_uint8*)src;
43800
43801 if (ditherMode == ma_dither_mode_none) {
43802 ma_uint64 i;
43803 for (i = 0; i < count; i += 1) {
43804 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
43805 ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);
43806 dst_s16[i] = (ma_int16)(dst_lo | dst_hi);
43807 }
43808 } else {
43809 ma_uint64 i;
43810 for (i = 0; i < count; i += 1) {
43811 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);
43812
43813 /* Dither. Don't overflow. */
43814 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
43815 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
43816 x = x + dither;
43817 } else {
43818 x = 0x7FFFFFFF;
43819 }
43820
43821 x = x >> 16;
43822 dst_s16[i] = (ma_int16)x;
43823 }
43824 }
43825 }
43826
43827 static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43828 {
43829 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
43830 }
43831
43832 #if defined(MA_SUPPORT_SSE2)
43833 static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43834 {
43835 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
43836 }
43837 #endif
43838 #if defined(MA_SUPPORT_NEON)
43839 static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43840 {
43841 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
43842 }
43843 #endif
43844
43845 MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43846 {
43847 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43848 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
43849 #else
43850 # if defined(MA_SUPPORT_SSE2)
43851 if (ma_has_sse2()) {
43852 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
43853 } else
43854 #elif defined(MA_SUPPORT_NEON)
43855 if (ma_has_neon()) {
43856 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
43857 } else
43858 #endif
43859 {
43860 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
43861 }
43862 #endif
43863 }
43864
43865
43866 MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43867 {
43868 (void)ditherMode;
43869
43870 ma_copy_memory_64(dst, src, count * 3);
43871 }
43872
43873
43874 static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43875 {
43876 ma_int32* dst_s32 = (ma_int32*)dst;
43877 const ma_uint8* src_s24 = (const ma_uint8*)src;
43878
43879 ma_uint64 i;
43880 for (i = 0; i < count; i += 1) {
43881 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);
43882 }
43883
43884 (void)ditherMode;
43885 }
43886
43887 static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43888 {
43889 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
43890 }
43891
43892 #if defined(MA_SUPPORT_SSE2)
43893 static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43894 {
43895 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
43896 }
43897 #endif
43898 #if defined(MA_SUPPORT_NEON)
43899 static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43900 {
43901 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
43902 }
43903 #endif
43904
43905 MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43906 {
43907 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43908 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
43909 #else
43910 # if defined(MA_SUPPORT_SSE2)
43911 if (ma_has_sse2()) {
43912 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
43913 } else
43914 #elif defined(MA_SUPPORT_NEON)
43915 if (ma_has_neon()) {
43916 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
43917 } else
43918 #endif
43919 {
43920 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
43921 }
43922 #endif
43923 }
43924
43925
43926 static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43927 {
43928 float* dst_f32 = (float*)dst;
43929 const ma_uint8* src_s24 = (const ma_uint8*)src;
43930
43931 ma_uint64 i;
43932 for (i = 0; i < count; i += 1) {
43933 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);
43934
43935 #if 0
43936 /* The accurate way. */
43937 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
43938 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
43939 x = x - 1; /* 0..2 to -1..1 */
43940 #else
43941 /* The fast way. */
43942 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
43943 #endif
43944
43945 dst_f32[i] = x;
43946 }
43947
43948 (void)ditherMode;
43949 }
43950
43951 static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43952 {
43953 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
43954 }
43955
43956 #if defined(MA_SUPPORT_SSE2)
43957 static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43958 {
43959 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
43960 }
43961 #endif
43962 #if defined(MA_SUPPORT_NEON)
43963 static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43964 {
43965 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
43966 }
43967 #endif
43968
43969 MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
43970 {
43971 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
43972 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
43973 #else
43974 # if defined(MA_SUPPORT_SSE2)
43975 if (ma_has_sse2()) {
43976 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
43977 } else
43978 #elif defined(MA_SUPPORT_NEON)
43979 if (ma_has_neon()) {
43980 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
43981 } else
43982 #endif
43983 {
43984 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
43985 }
43986 #endif
43987 }
43988
43989
43990 static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
43991 {
43992 ma_uint8* dst8 = (ma_uint8*)dst;
43993 const ma_uint8** src8 = (const ma_uint8**)src;
43994
43995 ma_uint64 iFrame;
43996 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
43997 ma_uint32 iChannel;
43998 for (iChannel = 0; iChannel < channels; iChannel += 1) {
43999 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
44000 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
44001 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
44002 }
44003 }
44004 }
44005
44006 static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44007 {
44008 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
44009 }
44010
44011 MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44012 {
44013 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44014 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
44015 #else
44016 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
44017 #endif
44018 }
44019
44020
44021 static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44022 {
44023 ma_uint8** dst8 = (ma_uint8**)dst;
44024 const ma_uint8* src8 = (const ma_uint8*)src;
44025
44026 ma_uint32 iFrame;
44027 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44028 ma_uint32 iChannel;
44029 for (iChannel = 0; iChannel < channels; iChannel += 1) {
44030 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
44031 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
44032 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
44033 }
44034 }
44035 }
44036
44037 static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44038 {
44039 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
44040 }
44041
44042 MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44043 {
44044 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44045 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
44046 #else
44047 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
44048 #endif
44049 }
44050
44051
44052
44053 /* s32 */
44054 static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44055 {
44056 ma_uint8* dst_u8 = (ma_uint8*)dst;
44057 const ma_int32* src_s32 = (const ma_int32*)src;
44058
44059 if (ditherMode == ma_dither_mode_none) {
44060 ma_uint64 i;
44061 for (i = 0; i < count; i += 1) {
44062 ma_int32 x = src_s32[i];
44063 x = x >> 24;
44064 x = x + 128;
44065 dst_u8[i] = (ma_uint8)x;
44066 }
44067 } else {
44068 ma_uint64 i;
44069 for (i = 0; i < count; i += 1) {
44070 ma_int32 x = src_s32[i];
44071
44072 /* Dither. Don't overflow. */
44073 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
44074 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
44075 x = x + dither;
44076 } else {
44077 x = 0x7FFFFFFF;
44078 }
44079
44080 x = x >> 24;
44081 x = x + 128;
44082 dst_u8[i] = (ma_uint8)x;
44083 }
44084 }
44085 }
44086
44087 static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44088 {
44089 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
44090 }
44091
44092 #if defined(MA_SUPPORT_SSE2)
44093 static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44094 {
44095 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
44096 }
44097 #endif
44098 #if defined(MA_SUPPORT_NEON)
44099 static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44100 {
44101 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
44102 }
44103 #endif
44104
44105 MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44106 {
44107 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44108 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
44109 #else
44110 # if defined(MA_SUPPORT_SSE2)
44111 if (ma_has_sse2()) {
44112 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
44113 } else
44114 #elif defined(MA_SUPPORT_NEON)
44115 if (ma_has_neon()) {
44116 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
44117 } else
44118 #endif
44119 {
44120 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
44121 }
44122 #endif
44123 }
44124
44125
44126 static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44127 {
44128 ma_int16* dst_s16 = (ma_int16*)dst;
44129 const ma_int32* src_s32 = (const ma_int32*)src;
44130
44131 if (ditherMode == ma_dither_mode_none) {
44132 ma_uint64 i;
44133 for (i = 0; i < count; i += 1) {
44134 ma_int32 x = src_s32[i];
44135 x = x >> 16;
44136 dst_s16[i] = (ma_int16)x;
44137 }
44138 } else {
44139 ma_uint64 i;
44140 for (i = 0; i < count; i += 1) {
44141 ma_int32 x = src_s32[i];
44142
44143 /* Dither. Don't overflow. */
44144 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
44145 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
44146 x = x + dither;
44147 } else {
44148 x = 0x7FFFFFFF;
44149 }
44150
44151 x = x >> 16;
44152 dst_s16[i] = (ma_int16)x;
44153 }
44154 }
44155 }
44156
44157 static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44158 {
44159 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
44160 }
44161
44162 #if defined(MA_SUPPORT_SSE2)
44163 static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44164 {
44165 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
44166 }
44167 #endif
44168 #if defined(MA_SUPPORT_NEON)
44169 static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44170 {
44171 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
44172 }
44173 #endif
44174
44175 MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44176 {
44177 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44178 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
44179 #else
44180 # if defined(MA_SUPPORT_SSE2)
44181 if (ma_has_sse2()) {
44182 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
44183 } else
44184 #elif defined(MA_SUPPORT_NEON)
44185 if (ma_has_neon()) {
44186 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
44187 } else
44188 #endif
44189 {
44190 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
44191 }
44192 #endif
44193 }
44194
44195
44196 static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44197 {
44198 ma_uint8* dst_s24 = (ma_uint8*)dst;
44199 const ma_int32* src_s32 = (const ma_int32*)src;
44200
44201 ma_uint64 i;
44202 for (i = 0; i < count; i += 1) {
44203 ma_uint32 x = (ma_uint32)src_s32[i];
44204 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
44205 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
44206 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
44207 }
44208
44209 (void)ditherMode; /* No dithering for s32 -> s24. */
44210 }
44211
44212 static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44213 {
44214 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
44215 }
44216
44217 #if defined(MA_SUPPORT_SSE2)
44218 static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44219 {
44220 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
44221 }
44222 #endif
44223 #if defined(MA_SUPPORT_NEON)
44224 static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44225 {
44226 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
44227 }
44228 #endif
44229
44230 MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44231 {
44232 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44233 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
44234 #else
44235 # if defined(MA_SUPPORT_SSE2)
44236 if (ma_has_sse2()) {
44237 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
44238 } else
44239 #elif defined(MA_SUPPORT_NEON)
44240 if (ma_has_neon()) {
44241 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
44242 } else
44243 #endif
44244 {
44245 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
44246 }
44247 #endif
44248 }
44249
44250
44251 MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44252 {
44253 (void)ditherMode;
44254
44255 ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
44256 }
44257
44258
44259 static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44260 {
44261 float* dst_f32 = (float*)dst;
44262 const ma_int32* src_s32 = (const ma_int32*)src;
44263
44264 ma_uint64 i;
44265 for (i = 0; i < count; i += 1) {
44266 double x = src_s32[i];
44267
44268 #if 0
44269 x = x + 2147483648.0;
44270 x = x * 0.0000000004656612873077392578125;
44271 x = x - 1;
44272 #else
44273 x = x / 2147483648.0;
44274 #endif
44275
44276 dst_f32[i] = (float)x;
44277 }
44278
44279 (void)ditherMode; /* No dithering for s32 -> f32. */
44280 }
44281
44282 static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44283 {
44284 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
44285 }
44286
44287 #if defined(MA_SUPPORT_SSE2)
44288 static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44289 {
44290 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
44291 }
44292 #endif
44293 #if defined(MA_SUPPORT_NEON)
44294 static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44295 {
44296 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
44297 }
44298 #endif
44299
44300 MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44301 {
44302 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44303 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
44304 #else
44305 # if defined(MA_SUPPORT_SSE2)
44306 if (ma_has_sse2()) {
44307 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
44308 } else
44309 #elif defined(MA_SUPPORT_NEON)
44310 if (ma_has_neon()) {
44311 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
44312 } else
44313 #endif
44314 {
44315 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
44316 }
44317 #endif
44318 }
44319
44320
44321 static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44322 {
44323 ma_int32* dst_s32 = (ma_int32*)dst;
44324 const ma_int32** src_s32 = (const ma_int32**)src;
44325
44326 ma_uint64 iFrame;
44327 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44328 ma_uint32 iChannel;
44329 for (iChannel = 0; iChannel < channels; iChannel += 1) {
44330 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
44331 }
44332 }
44333 }
44334
44335 static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44336 {
44337 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
44338 }
44339
44340 MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44341 {
44342 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44343 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
44344 #else
44345 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
44346 #endif
44347 }
44348
44349
44350 static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44351 {
44352 ma_int32** dst_s32 = (ma_int32**)dst;
44353 const ma_int32* src_s32 = (const ma_int32*)src;
44354
44355 ma_uint64 iFrame;
44356 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44357 ma_uint32 iChannel;
44358 for (iChannel = 0; iChannel < channels; iChannel += 1) {
44359 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
44360 }
44361 }
44362 }
44363
44364 static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44365 {
44366 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
44367 }
44368
44369 MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44370 {
44371 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44372 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
44373 #else
44374 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
44375 #endif
44376 }
44377
44378
44379 /* f32 */
44380 static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44381 {
44382 ma_uint64 i;
44383
44384 ma_uint8* dst_u8 = (ma_uint8*)dst;
44385 const float* src_f32 = (const float*)src;
44386
44387 float ditherMin = 0;
44388 float ditherMax = 0;
44389 if (ditherMode != ma_dither_mode_none) {
44390 ditherMin = 1.0f / -128;
44391 ditherMax = 1.0f / 127;
44392 }
44393
44394 for (i = 0; i < count; i += 1) {
44395 float x = src_f32[i];
44396 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44397 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44398 x = x + 1; /* -1..1 to 0..2 */
44399 x = x * 127.5f; /* 0..2 to 0..255 */
44400
44401 dst_u8[i] = (ma_uint8)x;
44402 }
44403 }
44404
44405 static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44406 {
44407 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
44408 }
44409
44410 #if defined(MA_SUPPORT_SSE2)
44411 static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44412 {
44413 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
44414 }
44415 #endif
44416 #if defined(MA_SUPPORT_NEON)
44417 static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44418 {
44419 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
44420 }
44421 #endif
44422
44423 MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44424 {
44425 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44426 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
44427 #else
44428 # if defined(MA_SUPPORT_SSE2)
44429 if (ma_has_sse2()) {
44430 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
44431 } else
44432 #elif defined(MA_SUPPORT_NEON)
44433 if (ma_has_neon()) {
44434 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
44435 } else
44436 #endif
44437 {
44438 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
44439 }
44440 #endif
44441 }
44442
44443 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44444 static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44445 {
44446 ma_uint64 i;
44447
44448 ma_int16* dst_s16 = (ma_int16*)dst;
44449 const float* src_f32 = (const float*)src;
44450
44451 float ditherMin = 0;
44452 float ditherMax = 0;
44453 if (ditherMode != ma_dither_mode_none) {
44454 ditherMin = 1.0f / -32768;
44455 ditherMax = 1.0f / 32767;
44456 }
44457
44458 for (i = 0; i < count; i += 1) {
44459 float x = src_f32[i];
44460 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44461 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44462
44463 #if 0
44464 /* The accurate way. */
44465 x = x + 1; /* -1..1 to 0..2 */
44466 x = x * 32767.5f; /* 0..2 to 0..65535 */
44467 x = x - 32768.0f; /* 0...65535 to -32768..32767 */
44468 #else
44469 /* The fast way. */
44470 x = x * 32767.0f; /* -1..1 to -32767..32767 */
44471 #endif
44472
44473 dst_s16[i] = (ma_int16)x;
44474 }
44475 }
44476 #else
44477 static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44478 {
44479 ma_uint64 i;
44480 ma_uint64 i4;
44481 ma_uint64 count4;
44482
44483 ma_int16* dst_s16 = (ma_int16*)dst;
44484 const float* src_f32 = (const float*)src;
44485
44486 float ditherMin = 0;
44487 float ditherMax = 0;
44488 if (ditherMode != ma_dither_mode_none) {
44489 ditherMin = 1.0f / -32768;
44490 ditherMax = 1.0f / 32767;
44491 }
44492
44493 /* Unrolled. */
44494 i = 0;
44495 count4 = count >> 2;
44496 for (i4 = 0; i4 < count4; i4 += 1) {
44497 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44498 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44499 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44500 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
44501
44502 float x0 = src_f32[i+0];
44503 float x1 = src_f32[i+1];
44504 float x2 = src_f32[i+2];
44505 float x3 = src_f32[i+3];
44506
44507 x0 = x0 + d0;
44508 x1 = x1 + d1;
44509 x2 = x2 + d2;
44510 x3 = x3 + d3;
44511
44512 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
44513 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
44514 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
44515 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
44516
44517 x0 = x0 * 32767.0f;
44518 x1 = x1 * 32767.0f;
44519 x2 = x2 * 32767.0f;
44520 x3 = x3 * 32767.0f;
44521
44522 dst_s16[i+0] = (ma_int16)x0;
44523 dst_s16[i+1] = (ma_int16)x1;
44524 dst_s16[i+2] = (ma_int16)x2;
44525 dst_s16[i+3] = (ma_int16)x3;
44526
44527 i += 4;
44528 }
44529
44530 /* Leftover. */
44531 for (; i < count; i += 1) {
44532 float x = src_f32[i];
44533 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44534 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44535 x = x * 32767.0f; /* -1..1 to -32767..32767 */
44536
44537 dst_s16[i] = (ma_int16)x;
44538 }
44539 }
44540
44541 #if defined(MA_SUPPORT_SSE2)
44542 static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44543 {
44544 ma_uint64 i;
44545 ma_uint64 i8;
44546 ma_uint64 count8;
44547 ma_int16* dst_s16;
44548 const float* src_f32;
44549 float ditherMin;
44550 float ditherMax;
44551
44552 /* Both the input and output buffers need to be aligned to 16 bytes. */
44553 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
44554 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44555 return;
44556 }
44557
44558 dst_s16 = (ma_int16*)dst;
44559 src_f32 = (const float*)src;
44560
44561 ditherMin = 0;
44562 ditherMax = 0;
44563 if (ditherMode != ma_dither_mode_none) {
44564 ditherMin = 1.0f / -32768;
44565 ditherMax = 1.0f / 32767;
44566 }
44567
44568 i = 0;
44569
44570 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
44571 count8 = count >> 3;
44572 for (i8 = 0; i8 < count8; i8 += 1) {
44573 __m128 d0;
44574 __m128 d1;
44575 __m128 x0;
44576 __m128 x1;
44577
44578 if (ditherMode == ma_dither_mode_none) {
44579 d0 = _mm_set1_ps(0);
44580 d1 = _mm_set1_ps(0);
44581 } else if (ditherMode == ma_dither_mode_rectangle) {
44582 d0 = _mm_set_ps(
44583 ma_dither_f32_rectangle(ditherMin, ditherMax),
44584 ma_dither_f32_rectangle(ditherMin, ditherMax),
44585 ma_dither_f32_rectangle(ditherMin, ditherMax),
44586 ma_dither_f32_rectangle(ditherMin, ditherMax)
44587 );
44588 d1 = _mm_set_ps(
44589 ma_dither_f32_rectangle(ditherMin, ditherMax),
44590 ma_dither_f32_rectangle(ditherMin, ditherMax),
44591 ma_dither_f32_rectangle(ditherMin, ditherMax),
44592 ma_dither_f32_rectangle(ditherMin, ditherMax)
44593 );
44594 } else {
44595 d0 = _mm_set_ps(
44596 ma_dither_f32_triangle(ditherMin, ditherMax),
44597 ma_dither_f32_triangle(ditherMin, ditherMax),
44598 ma_dither_f32_triangle(ditherMin, ditherMax),
44599 ma_dither_f32_triangle(ditherMin, ditherMax)
44600 );
44601 d1 = _mm_set_ps(
44602 ma_dither_f32_triangle(ditherMin, ditherMax),
44603 ma_dither_f32_triangle(ditherMin, ditherMax),
44604 ma_dither_f32_triangle(ditherMin, ditherMax),
44605 ma_dither_f32_triangle(ditherMin, ditherMax)
44606 );
44607 }
44608
44609 x0 = *((__m128*)(src_f32 + i) + 0);
44610 x1 = *((__m128*)(src_f32 + i) + 1);
44611
44612 x0 = _mm_add_ps(x0, d0);
44613 x1 = _mm_add_ps(x1, d1);
44614
44615 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
44616 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
44617
44618 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
44619
44620 i += 8;
44621 }
44622
44623
44624 /* Leftover. */
44625 for (; i < count; i += 1) {
44626 float x = src_f32[i];
44627 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44628 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44629 x = x * 32767.0f; /* -1..1 to -32767..32767 */
44630
44631 dst_s16[i] = (ma_int16)x;
44632 }
44633 }
44634 #endif /* SSE2 */
44635
44636 #if defined(MA_SUPPORT_NEON)
44637 static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44638 {
44639 ma_uint64 i;
44640 ma_uint64 i8;
44641 ma_uint64 count8;
44642 ma_int16* dst_s16;
44643 const float* src_f32;
44644 float ditherMin;
44645 float ditherMax;
44646
44647 if (!ma_has_neon()) {
44648 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44649 return;
44650 }
44651
44652 /* Both the input and output buffers need to be aligned to 16 bytes. */
44653 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
44654 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44655 return;
44656 }
44657
44658 dst_s16 = (ma_int16*)dst;
44659 src_f32 = (const float*)src;
44660
44661 ditherMin = 0;
44662 ditherMax = 0;
44663 if (ditherMode != ma_dither_mode_none) {
44664 ditherMin = 1.0f / -32768;
44665 ditherMax = 1.0f / 32767;
44666 }
44667
44668 i = 0;
44669
44670 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
44671 count8 = count >> 3;
44672 for (i8 = 0; i8 < count8; i8 += 1) {
44673 float32x4_t d0;
44674 float32x4_t d1;
44675 float32x4_t x0;
44676 float32x4_t x1;
44677 int32x4_t i0;
44678 int32x4_t i1;
44679
44680 if (ditherMode == ma_dither_mode_none) {
44681 d0 = vmovq_n_f32(0);
44682 d1 = vmovq_n_f32(0);
44683 } else if (ditherMode == ma_dither_mode_rectangle) {
44684 float d0v[4];
44685 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44686 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44687 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44688 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44689 d0 = vld1q_f32(d0v);
44690
44691 float d1v[4];
44692 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44693 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44694 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44695 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
44696 d1 = vld1q_f32(d1v);
44697 } else {
44698 float d0v[4];
44699 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
44700 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
44701 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
44702 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
44703 d0 = vld1q_f32(d0v);
44704
44705 float d1v[4];
44706 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
44707 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
44708 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
44709 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
44710 d1 = vld1q_f32(d1v);
44711 }
44712
44713 x0 = *((float32x4_t*)(src_f32 + i) + 0);
44714 x1 = *((float32x4_t*)(src_f32 + i) + 1);
44715
44716 x0 = vaddq_f32(x0, d0);
44717 x1 = vaddq_f32(x1, d1);
44718
44719 x0 = vmulq_n_f32(x0, 32767.0f);
44720 x1 = vmulq_n_f32(x1, 32767.0f);
44721
44722 i0 = vcvtq_s32_f32(x0);
44723 i1 = vcvtq_s32_f32(x1);
44724 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
44725
44726 i += 8;
44727 }
44728
44729
44730 /* Leftover. */
44731 for (; i < count; i += 1) {
44732 float x = src_f32[i];
44733 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
44734 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44735 x = x * 32767.0f; /* -1..1 to -32767..32767 */
44736
44737 dst_s16[i] = (ma_int16)x;
44738 }
44739 }
44740 #endif /* Neon */
44741 #endif /* MA_USE_REFERENCE_CONVERSION_APIS */
44742
44743 MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44744 {
44745 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44746 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
44747 #else
44748 # if defined(MA_SUPPORT_SSE2)
44749 if (ma_has_sse2()) {
44750 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
44751 } else
44752 #elif defined(MA_SUPPORT_NEON)
44753 if (ma_has_neon()) {
44754 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
44755 } else
44756 #endif
44757 {
44758 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
44759 }
44760 #endif
44761 }
44762
44763
44764 static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44765 {
44766 ma_uint8* dst_s24 = (ma_uint8*)dst;
44767 const float* src_f32 = (const float*)src;
44768
44769 ma_uint64 i;
44770 for (i = 0; i < count; i += 1) {
44771 ma_int32 r;
44772 float x = src_f32[i];
44773 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44774
44775 #if 0
44776 /* The accurate way. */
44777 x = x + 1; /* -1..1 to 0..2 */
44778 x = x * 8388607.5f; /* 0..2 to 0..16777215 */
44779 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
44780 #else
44781 /* The fast way. */
44782 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
44783 #endif
44784
44785 r = (ma_int32)x;
44786 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
44787 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
44788 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
44789 }
44790
44791 (void)ditherMode; /* No dithering for f32 -> s24. */
44792 }
44793
44794 static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44795 {
44796 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
44797 }
44798
44799 #if defined(MA_SUPPORT_SSE2)
44800 static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44801 {
44802 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
44803 }
44804 #endif
44805 #if defined(MA_SUPPORT_NEON)
44806 static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44807 {
44808 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
44809 }
44810 #endif
44811
44812 MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44813 {
44814 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44815 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
44816 #else
44817 # if defined(MA_SUPPORT_SSE2)
44818 if (ma_has_sse2()) {
44819 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
44820 } else
44821 #elif defined(MA_SUPPORT_NEON)
44822 if (ma_has_neon()) {
44823 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
44824 } else
44825 #endif
44826 {
44827 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
44828 }
44829 #endif
44830 }
44831
44832
44833 static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44834 {
44835 ma_int32* dst_s32 = (ma_int32*)dst;
44836 const float* src_f32 = (const float*)src;
44837
44838 ma_uint32 i;
44839 for (i = 0; i < count; i += 1) {
44840 double x = src_f32[i];
44841 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
44842
44843 #if 0
44844 /* The accurate way. */
44845 x = x + 1; /* -1..1 to 0..2 */
44846 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
44847 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
44848 #else
44849 /* The fast way. */
44850 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
44851 #endif
44852
44853 dst_s32[i] = (ma_int32)x;
44854 }
44855
44856 (void)ditherMode; /* No dithering for f32 -> s32. */
44857 }
44858
44859 static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44860 {
44861 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
44862 }
44863
44864 #if defined(MA_SUPPORT_SSE2)
44865 static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44866 {
44867 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
44868 }
44869 #endif
44870 #if defined(MA_SUPPORT_NEON)
44871 static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44872 {
44873 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
44874 }
44875 #endif
44876
44877 MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44878 {
44879 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44880 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
44881 #else
44882 # if defined(MA_SUPPORT_SSE2)
44883 if (ma_has_sse2()) {
44884 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
44885 } else
44886 #elif defined(MA_SUPPORT_NEON)
44887 if (ma_has_neon()) {
44888 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
44889 } else
44890 #endif
44891 {
44892 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
44893 }
44894 #endif
44895 }
44896
44897
44898 MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44899 {
44900 (void)ditherMode;
44901
44902 ma_copy_memory_64(dst, src, count * sizeof(float));
44903 }
44904
44905
44906 static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44907 {
44908 float* dst_f32 = (float*)dst;
44909 const float** src_f32 = (const float**)src;
44910
44911 ma_uint64 iFrame;
44912 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44913 ma_uint32 iChannel;
44914 for (iChannel = 0; iChannel < channels; iChannel += 1) {
44915 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
44916 }
44917 }
44918 }
44919
44920 static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44921 {
44922 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
44923 }
44924
44925 MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
44926 {
44927 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44928 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
44929 #else
44930 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
44931 #endif
44932 }
44933
44934
44935 static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44936 {
44937 float** dst_f32 = (float**)dst;
44938 const float* src_f32 = (const float*)src;
44939
44940 ma_uint64 iFrame;
44941 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44942 ma_uint32 iChannel;
44943 for (iChannel = 0; iChannel < channels; iChannel += 1) {
44944 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
44945 }
44946 }
44947 }
44948
44949 static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44950 {
44951 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
44952 }
44953
44954 MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
44955 {
44956 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
44957 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
44958 #else
44959 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
44960 #endif
44961 }
44962
44963
44964 MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
44965 {
44966 if (formatOut == formatIn) {
44967 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
44968 return;
44969 }
44970
44971 switch (formatIn)
44972 {
44973 case ma_format_u8:
44974 {
44975 switch (formatOut)
44976 {
44977 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
44978 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
44979 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
44980 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
44981 default: break;
44982 }
44983 } break;
44984
44985 case ma_format_s16:
44986 {
44987 switch (formatOut)
44988 {
44989 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
44990 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
44991 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
44992 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
44993 default: break;
44994 }
44995 } break;
44996
44997 case ma_format_s24:
44998 {
44999 switch (formatOut)
45000 {
45001 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
45002 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
45003 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
45004 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
45005 default: break;
45006 }
45007 } break;
45008
45009 case ma_format_s32:
45010 {
45011 switch (formatOut)
45012 {
45013 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
45014 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
45015 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
45016 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
45017 default: break;
45018 }
45019 } break;
45020
45021 case ma_format_f32:
45022 {
45023 switch (formatOut)
45024 {
45025 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
45026 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
45027 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
45028 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
45029 default: break;
45030 }
45031 } break;
45032
45033 default: break;
45034 }
45035 }
45036
45037 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)
45038 {
45039 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
45040 }
45041
45042 MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
45043 {
45044 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
45045 return; /* Invalid args. */
45046 }
45047
45048 /* For efficiency we do this per format. */
45049 switch (format) {
45050 case ma_format_s16:
45051 {
45052 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
45053 ma_uint64 iPCMFrame;
45054 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45055 ma_uint32 iChannel;
45056 for (iChannel = 0; iChannel < channels; ++iChannel) {
45057 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
45058 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
45059 }
45060 }
45061 } break;
45062
45063 case ma_format_f32:
45064 {
45065 const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
45066 ma_uint64 iPCMFrame;
45067 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45068 ma_uint32 iChannel;
45069 for (iChannel = 0; iChannel < channels; ++iChannel) {
45070 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
45071 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
45072 }
45073 }
45074 } break;
45075
45076 default:
45077 {
45078 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
45079 ma_uint64 iPCMFrame;
45080 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45081 ma_uint32 iChannel;
45082 for (iChannel = 0; iChannel < channels; ++iChannel) {
45083 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
45084 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
45085 memcpy(pDst, pSrc, sampleSizeInBytes);
45086 }
45087 }
45088 } break;
45089 }
45090 }
45091
45092 MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
45093 {
45094 switch (format)
45095 {
45096 case ma_format_s16:
45097 {
45098 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
45099 ma_uint64 iPCMFrame;
45100 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45101 ma_uint32 iChannel;
45102 for (iChannel = 0; iChannel < channels; ++iChannel) {
45103 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
45104 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
45105 }
45106 }
45107 } break;
45108
45109 case ma_format_f32:
45110 {
45111 float* pDstF32 = (float*)pInterleavedPCMFrames;
45112 ma_uint64 iPCMFrame;
45113 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45114 ma_uint32 iChannel;
45115 for (iChannel = 0; iChannel < channels; ++iChannel) {
45116 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
45117 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
45118 }
45119 }
45120 } break;
45121
45122 default:
45123 {
45124 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
45125 ma_uint64 iPCMFrame;
45126 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
45127 ma_uint32 iChannel;
45128 for (iChannel = 0; iChannel < channels; ++iChannel) {
45129 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
45130 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
45131 memcpy(pDst, pSrc, sampleSizeInBytes);
45132 }
45133 }
45134 } break;
45135 }
45136 }
45137
45138
45139 /**************************************************************************************************************************************************************
45140
45141 Biquad Filter
45142
45143 **************************************************************************************************************************************************************/
45144 #ifndef MA_BIQUAD_FIXED_POINT_SHIFT
45145 #define MA_BIQUAD_FIXED_POINT_SHIFT 14
45146 #endif
45147
45148 static ma_int32 ma_biquad_float_to_fp(double x)
45149 {
45150 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
45151 }
45152
45153 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)
45154 {
45155 ma_biquad_config config;
45156
45157 MA_ZERO_OBJECT(&config);
45158 config.format = format;
45159 config.channels = channels;
45160 config.b0 = b0;
45161 config.b1 = b1;
45162 config.b2 = b2;
45163 config.a0 = a0;
45164 config.a1 = a1;
45165 config.a2 = a2;
45166
45167 return config;
45168 }
45169
45170
45171 typedef struct
45172 {
45173 size_t sizeInBytes;
45174 size_t r1Offset;
45175 size_t r2Offset;
45176 } ma_biquad_heap_layout;
45177
45178 static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout)
45179 {
45180 MA_ASSERT(pHeapLayout != NULL);
45181
45182 MA_ZERO_OBJECT(pHeapLayout);
45183
45184 if (pConfig == NULL) {
45185 return MA_INVALID_ARGS;
45186 }
45187
45188 if (pConfig->channels == 0) {
45189 return MA_INVALID_ARGS;
45190 }
45191
45192 pHeapLayout->sizeInBytes = 0;
45193
45194 /* R0 */
45195 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
45196 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
45197
45198 /* R1 */
45199 pHeapLayout->r2Offset = pHeapLayout->sizeInBytes;
45200 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
45201
45202 /* Make sure allocation size is aligned. */
45203 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
45204
45205 return MA_SUCCESS;
45206 }
45207
45208 MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes)
45209 {
45210 ma_result result;
45211 ma_biquad_heap_layout heapLayout;
45212
45213 if (pHeapSizeInBytes == NULL) {
45214 return MA_INVALID_ARGS;
45215 }
45216
45217 *pHeapSizeInBytes = 0;
45218
45219 result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
45220 if (result != MA_SUCCESS) {
45221 return result;
45222 }
45223
45224 *pHeapSizeInBytes = heapLayout.sizeInBytes;
45225
45226 return MA_SUCCESS;
45227 }
45228
45229 MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ)
45230 {
45231 ma_result result;
45232 ma_biquad_heap_layout heapLayout;
45233
45234 if (pBQ == NULL) {
45235 return MA_INVALID_ARGS;
45236 }
45237
45238 MA_ZERO_OBJECT(pBQ);
45239
45240 result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
45241 if (result != MA_SUCCESS) {
45242 return result;
45243 }
45244
45245 pBQ->_pHeap = pHeap;
45246 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
45247
45248 pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
45249 pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset);
45250
45251 return ma_biquad_reinit(pConfig, pBQ);
45252 }
45253
45254 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ)
45255 {
45256 ma_result result;
45257 size_t heapSizeInBytes;
45258 void* pHeap;
45259
45260 result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes);
45261 if (result != MA_SUCCESS) {
45262 return result;
45263 }
45264
45265 if (heapSizeInBytes > 0) {
45266 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45267 if (pHeap == NULL) {
45268 return MA_OUT_OF_MEMORY;
45269 }
45270 } else {
45271 pHeap = NULL;
45272 }
45273
45274 result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ);
45275 if (result != MA_SUCCESS) {
45276 ma_free(pHeap, pAllocationCallbacks);
45277 return result;
45278 }
45279
45280 pBQ->_ownsHeap = MA_TRUE;
45281 return MA_SUCCESS;
45282 }
45283
45284 MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks)
45285 {
45286 if (pBQ == NULL) {
45287 return;
45288 }
45289
45290 if (pBQ->_ownsHeap) {
45291 ma_free(pBQ->_pHeap, pAllocationCallbacks);
45292 }
45293 }
45294
45295 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
45296 {
45297 if (pBQ == NULL || pConfig == NULL) {
45298 return MA_INVALID_ARGS;
45299 }
45300
45301 if (pConfig->a0 == 0) {
45302 return MA_INVALID_ARGS; /* Division by zero. */
45303 }
45304
45305 /* Only supporting f32 and s16. */
45306 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
45307 return MA_INVALID_ARGS;
45308 }
45309
45310 /* The format cannot be changed after initialization. */
45311 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
45312 return MA_INVALID_OPERATION;
45313 }
45314
45315 /* The channel count cannot be changed after initialization. */
45316 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
45317 return MA_INVALID_OPERATION;
45318 }
45319
45320
45321 pBQ->format = pConfig->format;
45322 pBQ->channels = pConfig->channels;
45323
45324 /* Normalize. */
45325 if (pConfig->format == ma_format_f32) {
45326 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
45327 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
45328 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
45329 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
45330 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
45331 } else {
45332 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
45333 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
45334 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
45335 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
45336 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
45337 }
45338
45339 return MA_SUCCESS;
45340 }
45341
45342 MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ)
45343 {
45344 if (pBQ == NULL) {
45345 return MA_INVALID_ARGS;
45346 }
45347
45348 if (pBQ->format == ma_format_f32) {
45349 pBQ->pR1->f32 = 0;
45350 pBQ->pR2->f32 = 0;
45351 } else {
45352 pBQ->pR1->s32 = 0;
45353 pBQ->pR2->s32 = 0;
45354 }
45355
45356 return MA_SUCCESS;
45357 }
45358
45359 static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
45360 {
45361 ma_uint32 c;
45362 const ma_uint32 channels = pBQ->channels;
45363 const float b0 = pBQ->b0.f32;
45364 const float b1 = pBQ->b1.f32;
45365 const float b2 = pBQ->b2.f32;
45366 const float a1 = pBQ->a1.f32;
45367 const float a2 = pBQ->a2.f32;
45368
45369 MA_ASSUME(channels > 0);
45370 for (c = 0; c < channels; c += 1) {
45371 float r1 = pBQ->pR1[c].f32;
45372 float r2 = pBQ->pR2[c].f32;
45373 float x = pX[c];
45374 float y;
45375
45376 y = b0*x + r1;
45377 r1 = b1*x - a1*y + r2;
45378 r2 = b2*x - a2*y;
45379
45380 pY[c] = y;
45381 pBQ->pR1[c].f32 = r1;
45382 pBQ->pR2[c].f32 = r2;
45383 }
45384 }
45385
45386 static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
45387 {
45388 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
45389 }
45390
45391 static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
45392 {
45393 ma_uint32 c;
45394 const ma_uint32 channels = pBQ->channels;
45395 const ma_int32 b0 = pBQ->b0.s32;
45396 const ma_int32 b1 = pBQ->b1.s32;
45397 const ma_int32 b2 = pBQ->b2.s32;
45398 const ma_int32 a1 = pBQ->a1.s32;
45399 const ma_int32 a2 = pBQ->a2.s32;
45400
45401 MA_ASSUME(channels > 0);
45402 for (c = 0; c < channels; c += 1) {
45403 ma_int32 r1 = pBQ->pR1[c].s32;
45404 ma_int32 r2 = pBQ->pR2[c].s32;
45405 ma_int32 x = pX[c];
45406 ma_int32 y;
45407
45408 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
45409 r1 = (b1*x - a1*y + r2);
45410 r2 = (b2*x - a2*y);
45411
45412 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
45413 pBQ->pR1[c].s32 = r1;
45414 pBQ->pR2[c].s32 = r2;
45415 }
45416 }
45417
45418 static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
45419 {
45420 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
45421 }
45422
45423 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45424 {
45425 ma_uint32 n;
45426
45427 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
45428 return MA_INVALID_ARGS;
45429 }
45430
45431 /* 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. */
45432
45433 if (pBQ->format == ma_format_f32) {
45434 /* */ float* pY = ( float*)pFramesOut;
45435 const float* pX = (const float*)pFramesIn;
45436
45437 for (n = 0; n < frameCount; n += 1) {
45438 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
45439 pY += pBQ->channels;
45440 pX += pBQ->channels;
45441 }
45442 } else if (pBQ->format == ma_format_s16) {
45443 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
45444 const ma_int16* pX = (const ma_int16*)pFramesIn;
45445
45446 for (n = 0; n < frameCount; n += 1) {
45447 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
45448 pY += pBQ->channels;
45449 pX += pBQ->channels;
45450 }
45451 } else {
45452 MA_ASSERT(MA_FALSE);
45453 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
45454 }
45455
45456 return MA_SUCCESS;
45457 }
45458
45459 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ)
45460 {
45461 if (pBQ == NULL) {
45462 return 0;
45463 }
45464
45465 return 2;
45466 }
45467
45468
45469 /**************************************************************************************************************************************************************
45470
45471 Low-Pass Filter
45472
45473 **************************************************************************************************************************************************************/
45474 MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
45475 {
45476 ma_lpf1_config config;
45477
45478 MA_ZERO_OBJECT(&config);
45479 config.format = format;
45480 config.channels = channels;
45481 config.sampleRate = sampleRate;
45482 config.cutoffFrequency = cutoffFrequency;
45483 config.q = 0.5;
45484
45485 return config;
45486 }
45487
45488 MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
45489 {
45490 ma_lpf2_config config;
45491
45492 MA_ZERO_OBJECT(&config);
45493 config.format = format;
45494 config.channels = channels;
45495 config.sampleRate = sampleRate;
45496 config.cutoffFrequency = cutoffFrequency;
45497 config.q = q;
45498
45499 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
45500 if (config.q == 0) {
45501 config.q = 0.707107;
45502 }
45503
45504 return config;
45505 }
45506
45507
45508 typedef struct
45509 {
45510 size_t sizeInBytes;
45511 size_t r1Offset;
45512 } ma_lpf1_heap_layout;
45513
45514 static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout)
45515 {
45516 MA_ASSERT(pHeapLayout != NULL);
45517
45518 MA_ZERO_OBJECT(pHeapLayout);
45519
45520 if (pConfig == NULL) {
45521 return MA_INVALID_ARGS;
45522 }
45523
45524 if (pConfig->channels == 0) {
45525 return MA_INVALID_ARGS;
45526 }
45527
45528 pHeapLayout->sizeInBytes = 0;
45529
45530 /* R1 */
45531 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
45532 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
45533
45534 /* Make sure allocation size is aligned. */
45535 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
45536
45537 return MA_SUCCESS;
45538 }
45539
45540 MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes)
45541 {
45542 ma_result result;
45543 ma_lpf1_heap_layout heapLayout;
45544
45545 if (pHeapSizeInBytes == NULL) {
45546 return MA_INVALID_ARGS;
45547 }
45548
45549 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
45550 if (result != MA_SUCCESS) {
45551 return result;
45552 }
45553
45554 *pHeapSizeInBytes = heapLayout.sizeInBytes;
45555
45556 return MA_SUCCESS;
45557 }
45558
45559 MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF)
45560 {
45561 ma_result result;
45562 ma_lpf1_heap_layout heapLayout;
45563
45564 if (pLPF == NULL) {
45565 return MA_INVALID_ARGS;
45566 }
45567
45568 MA_ZERO_OBJECT(pLPF);
45569
45570 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
45571 if (result != MA_SUCCESS) {
45572 return result;
45573 }
45574
45575 pLPF->_pHeap = pHeap;
45576 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
45577
45578 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
45579
45580 return ma_lpf1_reinit(pConfig, pLPF);
45581 }
45582
45583 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF)
45584 {
45585 ma_result result;
45586 size_t heapSizeInBytes;
45587 void* pHeap;
45588
45589 result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes);
45590 if (result != MA_SUCCESS) {
45591 return result;
45592 }
45593
45594 if (heapSizeInBytes > 0) {
45595 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45596 if (pHeap == NULL) {
45597 return MA_OUT_OF_MEMORY;
45598 }
45599 } else {
45600 pHeap = NULL;
45601 }
45602
45603 result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF);
45604 if (result != MA_SUCCESS) {
45605 ma_free(pHeap, pAllocationCallbacks);
45606 return result;
45607 }
45608
45609 pLPF->_ownsHeap = MA_TRUE;
45610 return MA_SUCCESS;
45611 }
45612
45613 MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
45614 {
45615 if (pLPF == NULL) {
45616 return;
45617 }
45618
45619 if (pLPF->_ownsHeap) {
45620 ma_free(pLPF->_pHeap, pAllocationCallbacks);
45621 }
45622 }
45623
45624 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
45625 {
45626 double a;
45627
45628 if (pLPF == NULL || pConfig == NULL) {
45629 return MA_INVALID_ARGS;
45630 }
45631
45632 /* Only supporting f32 and s16. */
45633 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
45634 return MA_INVALID_ARGS;
45635 }
45636
45637 /* The format cannot be changed after initialization. */
45638 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
45639 return MA_INVALID_OPERATION;
45640 }
45641
45642 /* The channel count cannot be changed after initialization. */
45643 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
45644 return MA_INVALID_OPERATION;
45645 }
45646
45647 pLPF->format = pConfig->format;
45648 pLPF->channels = pConfig->channels;
45649
45650 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
45651 if (pConfig->format == ma_format_f32) {
45652 pLPF->a.f32 = (float)a;
45653 } else {
45654 pLPF->a.s32 = ma_biquad_float_to_fp(a);
45655 }
45656
45657 return MA_SUCCESS;
45658 }
45659
45660 MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF)
45661 {
45662 if (pLPF == NULL) {
45663 return MA_INVALID_ARGS;
45664 }
45665
45666 if (pLPF->format == ma_format_f32) {
45667 pLPF->a.f32 = 0;
45668 } else {
45669 pLPF->a.s32 = 0;
45670 }
45671
45672 return MA_SUCCESS;
45673 }
45674
45675 static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
45676 {
45677 ma_uint32 c;
45678 const ma_uint32 channels = pLPF->channels;
45679 const float a = pLPF->a.f32;
45680 const float b = 1 - a;
45681
45682 MA_ASSUME(channels > 0);
45683 for (c = 0; c < channels; c += 1) {
45684 float r1 = pLPF->pR1[c].f32;
45685 float x = pX[c];
45686 float y;
45687
45688 y = b*x + a*r1;
45689
45690 pY[c] = y;
45691 pLPF->pR1[c].f32 = y;
45692 }
45693 }
45694
45695 static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
45696 {
45697 ma_uint32 c;
45698 const ma_uint32 channels = pLPF->channels;
45699 const ma_int32 a = pLPF->a.s32;
45700 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
45701
45702 MA_ASSUME(channels > 0);
45703 for (c = 0; c < channels; c += 1) {
45704 ma_int32 r1 = pLPF->pR1[c].s32;
45705 ma_int32 x = pX[c];
45706 ma_int32 y;
45707
45708 y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
45709
45710 pY[c] = (ma_int16)y;
45711 pLPF->pR1[c].s32 = (ma_int32)y;
45712 }
45713 }
45714
45715 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45716 {
45717 ma_uint32 n;
45718
45719 if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
45720 return MA_INVALID_ARGS;
45721 }
45722
45723 /* 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. */
45724
45725 if (pLPF->format == ma_format_f32) {
45726 /* */ float* pY = ( float*)pFramesOut;
45727 const float* pX = (const float*)pFramesIn;
45728
45729 for (n = 0; n < frameCount; n += 1) {
45730 ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
45731 pY += pLPF->channels;
45732 pX += pLPF->channels;
45733 }
45734 } else if (pLPF->format == ma_format_s16) {
45735 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
45736 const ma_int16* pX = (const ma_int16*)pFramesIn;
45737
45738 for (n = 0; n < frameCount; n += 1) {
45739 ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
45740 pY += pLPF->channels;
45741 pX += pLPF->channels;
45742 }
45743 } else {
45744 MA_ASSERT(MA_FALSE);
45745 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
45746 }
45747
45748 return MA_SUCCESS;
45749 }
45750
45751 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF)
45752 {
45753 if (pLPF == NULL) {
45754 return 0;
45755 }
45756
45757 return 1;
45758 }
45759
45760
45761 static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
45762 {
45763 ma_biquad_config bqConfig;
45764 double q;
45765 double w;
45766 double s;
45767 double c;
45768 double a;
45769
45770 MA_ASSERT(pConfig != NULL);
45771
45772 q = pConfig->q;
45773 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
45774 s = ma_sind(w);
45775 c = ma_cosd(w);
45776 a = s / (2*q);
45777
45778 bqConfig.b0 = (1 - c) / 2;
45779 bqConfig.b1 = 1 - c;
45780 bqConfig.b2 = (1 - c) / 2;
45781 bqConfig.a0 = 1 + a;
45782 bqConfig.a1 = -2 * c;
45783 bqConfig.a2 = 1 - a;
45784
45785 bqConfig.format = pConfig->format;
45786 bqConfig.channels = pConfig->channels;
45787
45788 return bqConfig;
45789 }
45790
45791 MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes)
45792 {
45793 ma_biquad_config bqConfig;
45794 bqConfig = ma_lpf2__get_biquad_config(pConfig);
45795
45796 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
45797 }
45798
45799 MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF)
45800 {
45801 ma_result result;
45802 ma_biquad_config bqConfig;
45803
45804 if (pLPF == NULL) {
45805 return MA_INVALID_ARGS;
45806 }
45807
45808 MA_ZERO_OBJECT(pLPF);
45809
45810 if (pConfig == NULL) {
45811 return MA_INVALID_ARGS;
45812 }
45813
45814 bqConfig = ma_lpf2__get_biquad_config(pConfig);
45815 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq);
45816 if (result != MA_SUCCESS) {
45817 return result;
45818 }
45819
45820 return MA_SUCCESS;
45821 }
45822
45823 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF)
45824 {
45825 ma_result result;
45826 size_t heapSizeInBytes;
45827 void* pHeap;
45828
45829 result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes);
45830 if (result != MA_SUCCESS) {
45831 return result;
45832 }
45833
45834 if (heapSizeInBytes > 0) {
45835 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
45836 if (pHeap == NULL) {
45837 return MA_OUT_OF_MEMORY;
45838 }
45839 } else {
45840 pHeap = NULL;
45841 }
45842
45843 result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF);
45844 if (result != MA_SUCCESS) {
45845 ma_free(pHeap, pAllocationCallbacks);
45846 return result;
45847 }
45848
45849 pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
45850 return MA_SUCCESS;
45851 }
45852
45853 MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
45854 {
45855 if (pLPF == NULL) {
45856 return;
45857 }
45858
45859 ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
45860 }
45861
45862 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
45863 {
45864 ma_result result;
45865 ma_biquad_config bqConfig;
45866
45867 if (pLPF == NULL || pConfig == NULL) {
45868 return MA_INVALID_ARGS;
45869 }
45870
45871 bqConfig = ma_lpf2__get_biquad_config(pConfig);
45872 result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
45873 if (result != MA_SUCCESS) {
45874 return result;
45875 }
45876
45877 return MA_SUCCESS;
45878 }
45879
45880 MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF)
45881 {
45882 if (pLPF == NULL) {
45883 return MA_INVALID_ARGS;
45884 }
45885
45886 ma_biquad_clear_cache(&pLPF->bq);
45887
45888 return MA_SUCCESS;
45889 }
45890
45891 static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
45892 {
45893 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
45894 }
45895
45896 static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
45897 {
45898 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
45899 }
45900
45901 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
45902 {
45903 if (pLPF == NULL) {
45904 return MA_INVALID_ARGS;
45905 }
45906
45907 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
45908 }
45909
45910 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF)
45911 {
45912 if (pLPF == NULL) {
45913 return 0;
45914 }
45915
45916 return ma_biquad_get_latency(&pLPF->bq);
45917 }
45918
45919
45920 MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
45921 {
45922 ma_lpf_config config;
45923
45924 MA_ZERO_OBJECT(&config);
45925 config.format = format;
45926 config.channels = channels;
45927 config.sampleRate = sampleRate;
45928 config.cutoffFrequency = cutoffFrequency;
45929 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
45930
45931 return config;
45932 }
45933
45934
45935 typedef struct
45936 {
45937 size_t sizeInBytes;
45938 size_t lpf1Offset;
45939 size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
45940 } ma_lpf_heap_layout;
45941
45942 static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count)
45943 {
45944 MA_ASSERT(pLPF1Count != NULL);
45945 MA_ASSERT(pLPF2Count != NULL);
45946
45947 *pLPF1Count = order % 2;
45948 *pLPF2Count = order / 2;
45949 }
45950
45951 static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout)
45952 {
45953 ma_result result;
45954 ma_uint32 lpf1Count;
45955 ma_uint32 lpf2Count;
45956 ma_uint32 ilpf1;
45957 ma_uint32 ilpf2;
45958
45959 MA_ASSERT(pHeapLayout != NULL);
45960
45961 MA_ZERO_OBJECT(pHeapLayout);
45962
45963 if (pConfig == NULL) {
45964 return MA_INVALID_ARGS;
45965 }
45966
45967 if (pConfig->channels == 0) {
45968 return MA_INVALID_ARGS;
45969 }
45970
45971 if (pConfig->order > MA_MAX_FILTER_ORDER) {
45972 return MA_INVALID_ARGS;
45973 }
45974
45975 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
45976
45977 pHeapLayout->sizeInBytes = 0;
45978
45979 /* LPF 1 */
45980 pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes;
45981 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
45982 size_t lpf1HeapSizeInBytes;
45983 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
45984
45985 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
45986 if (result != MA_SUCCESS) {
45987 return result;
45988 }
45989
45990 pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes;
45991 }
45992
45993 /* LPF 2*/
45994 pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes;
45995 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
45996 size_t lpf2HeapSizeInBytes;
45997 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. */
45998
45999 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
46000 if (result != MA_SUCCESS) {
46001 return result;
46002 }
46003
46004 pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes;
46005 }
46006
46007 /* Make sure allocation size is aligned. */
46008 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
46009
46010 return MA_SUCCESS;
46011 }
46012
46013 static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew)
46014 {
46015 ma_result result;
46016 ma_uint32 lpf1Count;
46017 ma_uint32 lpf2Count;
46018 ma_uint32 ilpf1;
46019 ma_uint32 ilpf2;
46020 ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */
46021
46022 if (pLPF == NULL || pConfig == NULL) {
46023 return MA_INVALID_ARGS;
46024 }
46025
46026 /* Only supporting f32 and s16. */
46027 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
46028 return MA_INVALID_ARGS;
46029 }
46030
46031 /* The format cannot be changed after initialization. */
46032 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
46033 return MA_INVALID_OPERATION;
46034 }
46035
46036 /* The channel count cannot be changed after initialization. */
46037 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
46038 return MA_INVALID_OPERATION;
46039 }
46040
46041 if (pConfig->order > MA_MAX_FILTER_ORDER) {
46042 return MA_INVALID_ARGS;
46043 }
46044
46045 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
46046
46047 /* The filter order can't change between reinits. */
46048 if (!isNew) {
46049 if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
46050 return MA_INVALID_OPERATION;
46051 }
46052 }
46053
46054 if (isNew) {
46055 result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
46056 if (result != MA_SUCCESS) {
46057 return result;
46058 }
46059
46060 pLPF->_pHeap = pHeap;
46061 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
46062
46063 pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset);
46064 pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset);
46065 } else {
46066 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
46067 }
46068
46069 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
46070 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
46071
46072 if (isNew) {
46073 size_t lpf1HeapSizeInBytes;
46074
46075 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
46076 if (result == MA_SUCCESS) {
46077 result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]);
46078 }
46079 } else {
46080 result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]);
46081 }
46082
46083 if (result != MA_SUCCESS) {
46084 ma_uint32 jlpf1;
46085
46086 for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) {
46087 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46088 }
46089
46090 return result;
46091 }
46092 }
46093
46094 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
46095 ma_lpf2_config lpf2Config;
46096 double q;
46097 double a;
46098
46099 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
46100 if (lpf1Count == 1) {
46101 a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
46102 } else {
46103 a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
46104 }
46105 q = 1 / (2*ma_cosd(a));
46106
46107 lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
46108
46109 if (isNew) {
46110 size_t lpf2HeapSizeInBytes;
46111
46112 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
46113 if (result == MA_SUCCESS) {
46114 result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]);
46115 }
46116 } else {
46117 result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]);
46118 }
46119
46120 if (result != MA_SUCCESS) {
46121 ma_uint32 jlpf1;
46122 ma_uint32 jlpf2;
46123
46124 for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) {
46125 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46126 }
46127
46128 for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) {
46129 ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46130 }
46131
46132 return result;
46133 }
46134 }
46135
46136 pLPF->lpf1Count = lpf1Count;
46137 pLPF->lpf2Count = lpf2Count;
46138 pLPF->format = pConfig->format;
46139 pLPF->channels = pConfig->channels;
46140 pLPF->sampleRate = pConfig->sampleRate;
46141
46142 return MA_SUCCESS;
46143 }
46144
46145 MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes)
46146 {
46147 ma_result result;
46148 ma_lpf_heap_layout heapLayout;
46149
46150 if (pHeapSizeInBytes == NULL) {
46151 return MA_INVALID_ARGS;
46152 }
46153
46154 *pHeapSizeInBytes = 0;
46155
46156 result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
46157 if (result != MA_SUCCESS) {
46158 return result;
46159 }
46160
46161 *pHeapSizeInBytes = heapLayout.sizeInBytes;
46162
46163 return result;
46164 }
46165
46166 MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF)
46167 {
46168 if (pLPF == NULL) {
46169 return MA_INVALID_ARGS;
46170 }
46171
46172 MA_ZERO_OBJECT(pLPF);
46173
46174 return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
46175 }
46176
46177 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF)
46178 {
46179 ma_result result;
46180 size_t heapSizeInBytes;
46181 void* pHeap;
46182
46183 result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes);
46184 if (result != MA_SUCCESS) {
46185 return result;
46186 }
46187
46188 if (heapSizeInBytes > 0) {
46189 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46190 if (pHeap == NULL) {
46191 return MA_OUT_OF_MEMORY;
46192 }
46193 } else {
46194 pHeap = NULL;
46195 }
46196
46197 result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF);
46198 if (result != MA_SUCCESS) {
46199 ma_free(pHeap, pAllocationCallbacks);
46200 return result;
46201 }
46202
46203 pLPF->_ownsHeap = MA_TRUE;
46204 return MA_SUCCESS;
46205 }
46206
46207 MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
46208 {
46209 ma_uint32 ilpf1;
46210 ma_uint32 ilpf2;
46211
46212 if (pLPF == NULL) {
46213 return;
46214 }
46215
46216 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46217 ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks);
46218 }
46219
46220 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46221 ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks);
46222 }
46223
46224 if (pLPF->_ownsHeap) {
46225 ma_free(pLPF->_pHeap, pAllocationCallbacks);
46226 }
46227 }
46228
46229 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
46230 {
46231 return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE);
46232 }
46233
46234 MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF)
46235 {
46236 ma_uint32 ilpf1;
46237 ma_uint32 ilpf2;
46238
46239 if (pLPF == NULL) {
46240 return MA_INVALID_ARGS;
46241 }
46242
46243 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46244 ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]);
46245 }
46246
46247 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46248 ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]);
46249 }
46250
46251 return MA_SUCCESS;
46252 }
46253
46254 static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
46255 {
46256 ma_uint32 ilpf1;
46257 ma_uint32 ilpf2;
46258
46259 MA_ASSERT(pLPF->format == ma_format_f32);
46260
46261 MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
46262
46263 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46264 ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY);
46265 }
46266
46267 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46268 ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY);
46269 }
46270 }
46271
46272 static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
46273 {
46274 ma_uint32 ilpf1;
46275 ma_uint32 ilpf2;
46276
46277 MA_ASSERT(pLPF->format == ma_format_s16);
46278
46279 MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
46280
46281 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46282 ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY);
46283 }
46284
46285 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46286 ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY);
46287 }
46288 }
46289
46290 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46291 {
46292 ma_result result;
46293 ma_uint32 ilpf1;
46294 ma_uint32 ilpf2;
46295
46296 if (pLPF == NULL) {
46297 return MA_INVALID_ARGS;
46298 }
46299
46300 /* Faster path for in-place. */
46301 if (pFramesOut == pFramesIn) {
46302 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
46303 result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount);
46304 if (result != MA_SUCCESS) {
46305 return result;
46306 }
46307 }
46308
46309 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
46310 result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount);
46311 if (result != MA_SUCCESS) {
46312 return result;
46313 }
46314 }
46315 }
46316
46317 /* Slightly slower path for copying. */
46318 if (pFramesOut != pFramesIn) {
46319 ma_uint32 iFrame;
46320
46321 /* */ if (pLPF->format == ma_format_f32) {
46322 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
46323 const float* pFramesInF32 = (const float*)pFramesIn;
46324
46325 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46326 ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
46327 pFramesOutF32 += pLPF->channels;
46328 pFramesInF32 += pLPF->channels;
46329 }
46330 } else if (pLPF->format == ma_format_s16) {
46331 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
46332 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
46333
46334 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46335 ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
46336 pFramesOutS16 += pLPF->channels;
46337 pFramesInS16 += pLPF->channels;
46338 }
46339 } else {
46340 MA_ASSERT(MA_FALSE);
46341 return MA_INVALID_OPERATION; /* Should never hit this. */
46342 }
46343 }
46344
46345 return MA_SUCCESS;
46346 }
46347
46348 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF)
46349 {
46350 if (pLPF == NULL) {
46351 return 0;
46352 }
46353
46354 return pLPF->lpf2Count*2 + pLPF->lpf1Count;
46355 }
46356
46357
46358 /**************************************************************************************************************************************************************
46359
46360 High-Pass Filtering
46361
46362 **************************************************************************************************************************************************************/
46363 MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
46364 {
46365 ma_hpf1_config config;
46366
46367 MA_ZERO_OBJECT(&config);
46368 config.format = format;
46369 config.channels = channels;
46370 config.sampleRate = sampleRate;
46371 config.cutoffFrequency = cutoffFrequency;
46372
46373 return config;
46374 }
46375
46376 MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
46377 {
46378 ma_hpf2_config config;
46379
46380 MA_ZERO_OBJECT(&config);
46381 config.format = format;
46382 config.channels = channels;
46383 config.sampleRate = sampleRate;
46384 config.cutoffFrequency = cutoffFrequency;
46385 config.q = q;
46386
46387 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
46388 if (config.q == 0) {
46389 config.q = 0.707107;
46390 }
46391
46392 return config;
46393 }
46394
46395
46396 typedef struct
46397 {
46398 size_t sizeInBytes;
46399 size_t r1Offset;
46400 } ma_hpf1_heap_layout;
46401
46402 static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout)
46403 {
46404 MA_ASSERT(pHeapLayout != NULL);
46405
46406 MA_ZERO_OBJECT(pHeapLayout);
46407
46408 if (pConfig == NULL) {
46409 return MA_INVALID_ARGS;
46410 }
46411
46412 if (pConfig->channels == 0) {
46413 return MA_INVALID_ARGS;
46414 }
46415
46416 pHeapLayout->sizeInBytes = 0;
46417
46418 /* R1 */
46419 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
46420 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
46421
46422 /* Make sure allocation size is aligned. */
46423 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
46424
46425 return MA_SUCCESS;
46426 }
46427
46428 MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes)
46429 {
46430 ma_result result;
46431 ma_hpf1_heap_layout heapLayout;
46432
46433 if (pHeapSizeInBytes == NULL) {
46434 return MA_INVALID_ARGS;
46435 }
46436
46437 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
46438 if (result != MA_SUCCESS) {
46439 return result;
46440 }
46441
46442 *pHeapSizeInBytes = heapLayout.sizeInBytes;
46443
46444 return MA_SUCCESS;
46445 }
46446
46447 MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF)
46448 {
46449 ma_result result;
46450 ma_hpf1_heap_layout heapLayout;
46451
46452 if (pLPF == NULL) {
46453 return MA_INVALID_ARGS;
46454 }
46455
46456 MA_ZERO_OBJECT(pLPF);
46457
46458 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
46459 if (result != MA_SUCCESS) {
46460 return result;
46461 }
46462
46463 pLPF->_pHeap = pHeap;
46464 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
46465
46466 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
46467
46468 return ma_hpf1_reinit(pConfig, pLPF);
46469 }
46470
46471 MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF)
46472 {
46473 ma_result result;
46474 size_t heapSizeInBytes;
46475 void* pHeap;
46476
46477 result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes);
46478 if (result != MA_SUCCESS) {
46479 return result;
46480 }
46481
46482 if (heapSizeInBytes > 0) {
46483 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46484 if (pHeap == NULL) {
46485 return MA_OUT_OF_MEMORY;
46486 }
46487 } else {
46488 pHeap = NULL;
46489 }
46490
46491 result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF);
46492 if (result != MA_SUCCESS) {
46493 ma_free(pHeap, pAllocationCallbacks);
46494 return result;
46495 }
46496
46497 pLPF->_ownsHeap = MA_TRUE;
46498 return MA_SUCCESS;
46499 }
46500
46501 MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
46502 {
46503 if (pHPF == NULL) {
46504 return;
46505 }
46506
46507 if (pHPF->_ownsHeap) {
46508 ma_free(pHPF->_pHeap, pAllocationCallbacks);
46509 }
46510 }
46511
46512 MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
46513 {
46514 double a;
46515
46516 if (pHPF == NULL || pConfig == NULL) {
46517 return MA_INVALID_ARGS;
46518 }
46519
46520 /* Only supporting f32 and s16. */
46521 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
46522 return MA_INVALID_ARGS;
46523 }
46524
46525 /* The format cannot be changed after initialization. */
46526 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
46527 return MA_INVALID_OPERATION;
46528 }
46529
46530 /* The channel count cannot be changed after initialization. */
46531 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
46532 return MA_INVALID_OPERATION;
46533 }
46534
46535 pHPF->format = pConfig->format;
46536 pHPF->channels = pConfig->channels;
46537
46538 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
46539 if (pConfig->format == ma_format_f32) {
46540 pHPF->a.f32 = (float)a;
46541 } else {
46542 pHPF->a.s32 = ma_biquad_float_to_fp(a);
46543 }
46544
46545 return MA_SUCCESS;
46546 }
46547
46548 static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
46549 {
46550 ma_uint32 c;
46551 const ma_uint32 channels = pHPF->channels;
46552 const float a = 1 - pHPF->a.f32;
46553 const float b = 1 - a;
46554
46555 MA_ASSUME(channels > 0);
46556 for (c = 0; c < channels; c += 1) {
46557 float r1 = pHPF->pR1[c].f32;
46558 float x = pX[c];
46559 float y;
46560
46561 y = b*x - a*r1;
46562
46563 pY[c] = y;
46564 pHPF->pR1[c].f32 = y;
46565 }
46566 }
46567
46568 static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
46569 {
46570 ma_uint32 c;
46571 const ma_uint32 channels = pHPF->channels;
46572 const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
46573 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
46574
46575 MA_ASSUME(channels > 0);
46576 for (c = 0; c < channels; c += 1) {
46577 ma_int32 r1 = pHPF->pR1[c].s32;
46578 ma_int32 x = pX[c];
46579 ma_int32 y;
46580
46581 y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
46582
46583 pY[c] = (ma_int16)y;
46584 pHPF->pR1[c].s32 = (ma_int32)y;
46585 }
46586 }
46587
46588 MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46589 {
46590 ma_uint32 n;
46591
46592 if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
46593 return MA_INVALID_ARGS;
46594 }
46595
46596 /* 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. */
46597
46598 if (pHPF->format == ma_format_f32) {
46599 /* */ float* pY = ( float*)pFramesOut;
46600 const float* pX = (const float*)pFramesIn;
46601
46602 for (n = 0; n < frameCount; n += 1) {
46603 ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
46604 pY += pHPF->channels;
46605 pX += pHPF->channels;
46606 }
46607 } else if (pHPF->format == ma_format_s16) {
46608 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
46609 const ma_int16* pX = (const ma_int16*)pFramesIn;
46610
46611 for (n = 0; n < frameCount; n += 1) {
46612 ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
46613 pY += pHPF->channels;
46614 pX += pHPF->channels;
46615 }
46616 } else {
46617 MA_ASSERT(MA_FALSE);
46618 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
46619 }
46620
46621 return MA_SUCCESS;
46622 }
46623
46624 MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF)
46625 {
46626 if (pHPF == NULL) {
46627 return 0;
46628 }
46629
46630 return 1;
46631 }
46632
46633
46634 static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
46635 {
46636 ma_biquad_config bqConfig;
46637 double q;
46638 double w;
46639 double s;
46640 double c;
46641 double a;
46642
46643 MA_ASSERT(pConfig != NULL);
46644
46645 q = pConfig->q;
46646 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
46647 s = ma_sind(w);
46648 c = ma_cosd(w);
46649 a = s / (2*q);
46650
46651 bqConfig.b0 = (1 + c) / 2;
46652 bqConfig.b1 = -(1 + c);
46653 bqConfig.b2 = (1 + c) / 2;
46654 bqConfig.a0 = 1 + a;
46655 bqConfig.a1 = -2 * c;
46656 bqConfig.a2 = 1 - a;
46657
46658 bqConfig.format = pConfig->format;
46659 bqConfig.channels = pConfig->channels;
46660
46661 return bqConfig;
46662 }
46663
46664 MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes)
46665 {
46666 ma_biquad_config bqConfig;
46667 bqConfig = ma_hpf2__get_biquad_config(pConfig);
46668
46669 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
46670 }
46671
46672 MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF)
46673 {
46674 ma_result result;
46675 ma_biquad_config bqConfig;
46676
46677 if (pHPF == NULL) {
46678 return MA_INVALID_ARGS;
46679 }
46680
46681 MA_ZERO_OBJECT(pHPF);
46682
46683 if (pConfig == NULL) {
46684 return MA_INVALID_ARGS;
46685 }
46686
46687 bqConfig = ma_hpf2__get_biquad_config(pConfig);
46688 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq);
46689 if (result != MA_SUCCESS) {
46690 return result;
46691 }
46692
46693 return MA_SUCCESS;
46694 }
46695
46696 MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF)
46697 {
46698 ma_result result;
46699 size_t heapSizeInBytes;
46700 void* pHeap;
46701
46702 result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes);
46703 if (result != MA_SUCCESS) {
46704 return result;
46705 }
46706
46707 if (heapSizeInBytes > 0) {
46708 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
46709 if (pHeap == NULL) {
46710 return MA_OUT_OF_MEMORY;
46711 }
46712 } else {
46713 pHeap = NULL;
46714 }
46715
46716 result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF);
46717 if (result != MA_SUCCESS) {
46718 ma_free(pHeap, pAllocationCallbacks);
46719 return result;
46720 }
46721
46722 pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
46723 return MA_SUCCESS;
46724 }
46725
46726 MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
46727 {
46728 if (pHPF == NULL) {
46729 return;
46730 }
46731
46732 ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
46733 }
46734
46735 MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
46736 {
46737 ma_result result;
46738 ma_biquad_config bqConfig;
46739
46740 if (pHPF == NULL || pConfig == NULL) {
46741 return MA_INVALID_ARGS;
46742 }
46743
46744 bqConfig = ma_hpf2__get_biquad_config(pConfig);
46745 result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
46746 if (result != MA_SUCCESS) {
46747 return result;
46748 }
46749
46750 return MA_SUCCESS;
46751 }
46752
46753 static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
46754 {
46755 ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
46756 }
46757
46758 static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
46759 {
46760 ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
46761 }
46762
46763 MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
46764 {
46765 if (pHPF == NULL) {
46766 return MA_INVALID_ARGS;
46767 }
46768
46769 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
46770 }
46771
46772 MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF)
46773 {
46774 if (pHPF == NULL) {
46775 return 0;
46776 }
46777
46778 return ma_biquad_get_latency(&pHPF->bq);
46779 }
46780
46781
46782 MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
46783 {
46784 ma_hpf_config config;
46785
46786 MA_ZERO_OBJECT(&config);
46787 config.format = format;
46788 config.channels = channels;
46789 config.sampleRate = sampleRate;
46790 config.cutoffFrequency = cutoffFrequency;
46791 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
46792
46793 return config;
46794 }
46795
46796
46797 typedef struct
46798 {
46799 size_t sizeInBytes;
46800 size_t hpf1Offset;
46801 size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
46802 } ma_hpf_heap_layout;
46803
46804 static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count)
46805 {
46806 MA_ASSERT(pHPF1Count != NULL);
46807 MA_ASSERT(pHPF2Count != NULL);
46808
46809 *pHPF1Count = order % 2;
46810 *pHPF2Count = order / 2;
46811 }
46812
46813 static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout)
46814 {
46815 ma_result result;
46816 ma_uint32 hpf1Count;
46817 ma_uint32 hpf2Count;
46818 ma_uint32 ihpf1;
46819 ma_uint32 ihpf2;
46820
46821 MA_ASSERT(pHeapLayout != NULL);
46822
46823 MA_ZERO_OBJECT(pHeapLayout);
46824
46825 if (pConfig == NULL) {
46826 return MA_INVALID_ARGS;
46827 }
46828
46829 if (pConfig->channels == 0) {
46830 return MA_INVALID_ARGS;
46831 }
46832
46833 if (pConfig->order > MA_MAX_FILTER_ORDER) {
46834 return MA_INVALID_ARGS;
46835 }
46836
46837 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
46838
46839 pHeapLayout->sizeInBytes = 0;
46840
46841 /* HPF 1 */
46842 pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes;
46843 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
46844 size_t hpf1HeapSizeInBytes;
46845 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
46846
46847 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
46848 if (result != MA_SUCCESS) {
46849 return result;
46850 }
46851
46852 pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes;
46853 }
46854
46855 /* HPF 2*/
46856 pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes;
46857 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
46858 size_t hpf2HeapSizeInBytes;
46859 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. */
46860
46861 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
46862 if (result != MA_SUCCESS) {
46863 return result;
46864 }
46865
46866 pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes;
46867 }
46868
46869 /* Make sure allocation size is aligned. */
46870 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
46871
46872 return MA_SUCCESS;
46873 }
46874
46875 static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew)
46876 {
46877 ma_result result;
46878 ma_uint32 hpf1Count;
46879 ma_uint32 hpf2Count;
46880 ma_uint32 ihpf1;
46881 ma_uint32 ihpf2;
46882 ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */
46883
46884 if (pHPF == NULL || pConfig == NULL) {
46885 return MA_INVALID_ARGS;
46886 }
46887
46888 /* Only supporting f32 and s16. */
46889 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
46890 return MA_INVALID_ARGS;
46891 }
46892
46893 /* The format cannot be changed after initialization. */
46894 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
46895 return MA_INVALID_OPERATION;
46896 }
46897
46898 /* The channel count cannot be changed after initialization. */
46899 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
46900 return MA_INVALID_OPERATION;
46901 }
46902
46903 if (pConfig->order > MA_MAX_FILTER_ORDER) {
46904 return MA_INVALID_ARGS;
46905 }
46906
46907 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
46908
46909 /* The filter order can't change between reinits. */
46910 if (!isNew) {
46911 if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
46912 return MA_INVALID_OPERATION;
46913 }
46914 }
46915
46916 if (isNew) {
46917 result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
46918 if (result != MA_SUCCESS) {
46919 return result;
46920 }
46921
46922 pHPF->_pHeap = pHeap;
46923 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
46924
46925 pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset);
46926 pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset);
46927 } else {
46928 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
46929 }
46930
46931 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
46932 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
46933
46934 if (isNew) {
46935 size_t hpf1HeapSizeInBytes;
46936
46937 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
46938 if (result == MA_SUCCESS) {
46939 result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]);
46940 }
46941 } else {
46942 result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]);
46943 }
46944
46945 if (result != MA_SUCCESS) {
46946 ma_uint32 jhpf1;
46947
46948 for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) {
46949 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46950 }
46951
46952 return result;
46953 }
46954 }
46955
46956 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
46957 ma_hpf2_config hpf2Config;
46958 double q;
46959 double a;
46960
46961 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
46962 if (hpf1Count == 1) {
46963 a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
46964 } else {
46965 a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
46966 }
46967 q = 1 / (2*ma_cosd(a));
46968
46969 hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
46970
46971 if (isNew) {
46972 size_t hpf2HeapSizeInBytes;
46973
46974 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
46975 if (result == MA_SUCCESS) {
46976 result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]);
46977 }
46978 } else {
46979 result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]);
46980 }
46981
46982 if (result != MA_SUCCESS) {
46983 ma_uint32 jhpf1;
46984 ma_uint32 jhpf2;
46985
46986 for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) {
46987 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46988 }
46989
46990 for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) {
46991 ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
46992 }
46993
46994 return result;
46995 }
46996 }
46997
46998 pHPF->hpf1Count = hpf1Count;
46999 pHPF->hpf2Count = hpf2Count;
47000 pHPF->format = pConfig->format;
47001 pHPF->channels = pConfig->channels;
47002 pHPF->sampleRate = pConfig->sampleRate;
47003
47004 return MA_SUCCESS;
47005 }
47006
47007 MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes)
47008 {
47009 ma_result result;
47010 ma_hpf_heap_layout heapLayout;
47011
47012 if (pHeapSizeInBytes == NULL) {
47013 return MA_INVALID_ARGS;
47014 }
47015
47016 *pHeapSizeInBytes = 0;
47017
47018 result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
47019 if (result != MA_SUCCESS) {
47020 return result;
47021 }
47022
47023 *pHeapSizeInBytes = heapLayout.sizeInBytes;
47024
47025 return result;
47026 }
47027
47028 MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF)
47029 {
47030 if (pLPF == NULL) {
47031 return MA_INVALID_ARGS;
47032 }
47033
47034 MA_ZERO_OBJECT(pLPF);
47035
47036 return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
47037 }
47038
47039 MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF)
47040 {
47041 ma_result result;
47042 size_t heapSizeInBytes;
47043 void* pHeap;
47044
47045 result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes);
47046 if (result != MA_SUCCESS) {
47047 return result;
47048 }
47049
47050 if (heapSizeInBytes > 0) {
47051 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47052 if (pHeap == NULL) {
47053 return MA_OUT_OF_MEMORY;
47054 }
47055 } else {
47056 pHeap = NULL;
47057 }
47058
47059 result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF);
47060 if (result != MA_SUCCESS) {
47061 ma_free(pHeap, pAllocationCallbacks);
47062 return result;
47063 }
47064
47065 pHPF->_ownsHeap = MA_TRUE;
47066 return MA_SUCCESS;
47067 }
47068
47069 MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
47070 {
47071 ma_uint32 ihpf1;
47072 ma_uint32 ihpf2;
47073
47074 if (pHPF == NULL) {
47075 return;
47076 }
47077
47078 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47079 ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks);
47080 }
47081
47082 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47083 ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks);
47084 }
47085
47086 if (pHPF->_ownsHeap) {
47087 ma_free(pHPF->_pHeap, pAllocationCallbacks);
47088 }
47089 }
47090
47091 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
47092 {
47093 return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE);
47094 }
47095
47096 MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47097 {
47098 ma_result result;
47099 ma_uint32 ihpf1;
47100 ma_uint32 ihpf2;
47101
47102 if (pHPF == NULL) {
47103 return MA_INVALID_ARGS;
47104 }
47105
47106 /* Faster path for in-place. */
47107 if (pFramesOut == pFramesIn) {
47108 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47109 result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount);
47110 if (result != MA_SUCCESS) {
47111 return result;
47112 }
47113 }
47114
47115 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47116 result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount);
47117 if (result != MA_SUCCESS) {
47118 return result;
47119 }
47120 }
47121 }
47122
47123 /* Slightly slower path for copying. */
47124 if (pFramesOut != pFramesIn) {
47125 ma_uint32 iFrame;
47126
47127 /* */ if (pHPF->format == ma_format_f32) {
47128 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
47129 const float* pFramesInF32 = (const float*)pFramesIn;
47130
47131 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47132 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
47133
47134 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47135 ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32);
47136 }
47137
47138 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47139 ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32);
47140 }
47141
47142 pFramesOutF32 += pHPF->channels;
47143 pFramesInF32 += pHPF->channels;
47144 }
47145 } else if (pHPF->format == ma_format_s16) {
47146 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
47147 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
47148
47149 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47150 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
47151
47152 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
47153 ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16);
47154 }
47155
47156 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
47157 ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16);
47158 }
47159
47160 pFramesOutS16 += pHPF->channels;
47161 pFramesInS16 += pHPF->channels;
47162 }
47163 } else {
47164 MA_ASSERT(MA_FALSE);
47165 return MA_INVALID_OPERATION; /* Should never hit this. */
47166 }
47167 }
47168
47169 return MA_SUCCESS;
47170 }
47171
47172 MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF)
47173 {
47174 if (pHPF == NULL) {
47175 return 0;
47176 }
47177
47178 return pHPF->hpf2Count*2 + pHPF->hpf1Count;
47179 }
47180
47181
47182 /**************************************************************************************************************************************************************
47183
47184 Band-Pass Filtering
47185
47186 **************************************************************************************************************************************************************/
47187 MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
47188 {
47189 ma_bpf2_config config;
47190
47191 MA_ZERO_OBJECT(&config);
47192 config.format = format;
47193 config.channels = channels;
47194 config.sampleRate = sampleRate;
47195 config.cutoffFrequency = cutoffFrequency;
47196 config.q = q;
47197
47198 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
47199 if (config.q == 0) {
47200 config.q = 0.707107;
47201 }
47202
47203 return config;
47204 }
47205
47206
47207 static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
47208 {
47209 ma_biquad_config bqConfig;
47210 double q;
47211 double w;
47212 double s;
47213 double c;
47214 double a;
47215
47216 MA_ASSERT(pConfig != NULL);
47217
47218 q = pConfig->q;
47219 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
47220 s = ma_sind(w);
47221 c = ma_cosd(w);
47222 a = s / (2*q);
47223
47224 bqConfig.b0 = q * a;
47225 bqConfig.b1 = 0;
47226 bqConfig.b2 = -q * a;
47227 bqConfig.a0 = 1 + a;
47228 bqConfig.a1 = -2 * c;
47229 bqConfig.a2 = 1 - a;
47230
47231 bqConfig.format = pConfig->format;
47232 bqConfig.channels = pConfig->channels;
47233
47234 return bqConfig;
47235 }
47236
47237 MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes)
47238 {
47239 ma_biquad_config bqConfig;
47240 bqConfig = ma_bpf2__get_biquad_config(pConfig);
47241
47242 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
47243 }
47244
47245 MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF)
47246 {
47247 ma_result result;
47248 ma_biquad_config bqConfig;
47249
47250 if (pBPF == NULL) {
47251 return MA_INVALID_ARGS;
47252 }
47253
47254 MA_ZERO_OBJECT(pBPF);
47255
47256 if (pConfig == NULL) {
47257 return MA_INVALID_ARGS;
47258 }
47259
47260 bqConfig = ma_bpf2__get_biquad_config(pConfig);
47261 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq);
47262 if (result != MA_SUCCESS) {
47263 return result;
47264 }
47265
47266 return MA_SUCCESS;
47267 }
47268
47269 MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF)
47270 {
47271 ma_result result;
47272 size_t heapSizeInBytes;
47273 void* pHeap;
47274
47275 result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes);
47276 if (result != MA_SUCCESS) {
47277 return result;
47278 }
47279
47280 if (heapSizeInBytes > 0) {
47281 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47282 if (pHeap == NULL) {
47283 return MA_OUT_OF_MEMORY;
47284 }
47285 } else {
47286 pHeap = NULL;
47287 }
47288
47289 result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF);
47290 if (result != MA_SUCCESS) {
47291 ma_free(pHeap, pAllocationCallbacks);
47292 return result;
47293 }
47294
47295 pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
47296 return MA_SUCCESS;
47297 }
47298
47299 MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
47300 {
47301 if (pBPF == NULL) {
47302 return;
47303 }
47304
47305 ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
47306 }
47307
47308 MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
47309 {
47310 ma_result result;
47311 ma_biquad_config bqConfig;
47312
47313 if (pBPF == NULL || pConfig == NULL) {
47314 return MA_INVALID_ARGS;
47315 }
47316
47317 bqConfig = ma_bpf2__get_biquad_config(pConfig);
47318 result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
47319 if (result != MA_SUCCESS) {
47320 return result;
47321 }
47322
47323 return MA_SUCCESS;
47324 }
47325
47326 static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
47327 {
47328 ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
47329 }
47330
47331 static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
47332 {
47333 ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
47334 }
47335
47336 MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47337 {
47338 if (pBPF == NULL) {
47339 return MA_INVALID_ARGS;
47340 }
47341
47342 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
47343 }
47344
47345 MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF)
47346 {
47347 if (pBPF == NULL) {
47348 return 0;
47349 }
47350
47351 return ma_biquad_get_latency(&pBPF->bq);
47352 }
47353
47354
47355 MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
47356 {
47357 ma_bpf_config config;
47358
47359 MA_ZERO_OBJECT(&config);
47360 config.format = format;
47361 config.channels = channels;
47362 config.sampleRate = sampleRate;
47363 config.cutoffFrequency = cutoffFrequency;
47364 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
47365
47366 return config;
47367 }
47368
47369
47370 typedef struct
47371 {
47372 size_t sizeInBytes;
47373 size_t bpf2Offset;
47374 } ma_bpf_heap_layout;
47375
47376 static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout)
47377 {
47378 ma_result result;
47379 ma_uint32 bpf2Count;
47380 ma_uint32 ibpf2;
47381
47382 MA_ASSERT(pHeapLayout != NULL);
47383
47384 MA_ZERO_OBJECT(pHeapLayout);
47385
47386 if (pConfig == NULL) {
47387 return MA_INVALID_ARGS;
47388 }
47389
47390 if (pConfig->order > MA_MAX_FILTER_ORDER) {
47391 return MA_INVALID_ARGS;
47392 }
47393
47394 /* We must have an even number of order. */
47395 if ((pConfig->order & 0x1) != 0) {
47396 return MA_INVALID_ARGS;
47397 }
47398
47399 bpf2Count = pConfig->channels / 2;
47400
47401 pHeapLayout->sizeInBytes = 0;
47402
47403 /* BPF 2 */
47404 pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes;
47405 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
47406 size_t bpf2HeapSizeInBytes;
47407 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. */
47408
47409 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
47410 if (result != MA_SUCCESS) {
47411 return result;
47412 }
47413
47414 pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes;
47415 }
47416
47417 /* Make sure allocation size is aligned. */
47418 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
47419
47420 return MA_SUCCESS;
47421 }
47422
47423 static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew)
47424 {
47425 ma_result result;
47426 ma_uint32 bpf2Count;
47427 ma_uint32 ibpf2;
47428 ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */
47429
47430 if (pBPF == NULL || pConfig == NULL) {
47431 return MA_INVALID_ARGS;
47432 }
47433
47434 /* Only supporting f32 and s16. */
47435 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
47436 return MA_INVALID_ARGS;
47437 }
47438
47439 /* The format cannot be changed after initialization. */
47440 if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
47441 return MA_INVALID_OPERATION;
47442 }
47443
47444 /* The channel count cannot be changed after initialization. */
47445 if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
47446 return MA_INVALID_OPERATION;
47447 }
47448
47449 if (pConfig->order > MA_MAX_FILTER_ORDER) {
47450 return MA_INVALID_ARGS;
47451 }
47452
47453 /* We must have an even number of order. */
47454 if ((pConfig->order & 0x1) != 0) {
47455 return MA_INVALID_ARGS;
47456 }
47457
47458 bpf2Count = pConfig->order / 2;
47459
47460 /* The filter order can't change between reinits. */
47461 if (!isNew) {
47462 if (pBPF->bpf2Count != bpf2Count) {
47463 return MA_INVALID_OPERATION;
47464 }
47465 }
47466
47467 if (isNew) {
47468 result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
47469 if (result != MA_SUCCESS) {
47470 return result;
47471 }
47472
47473 pBPF->_pHeap = pHeap;
47474 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
47475
47476 pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset);
47477 } else {
47478 MA_ZERO_OBJECT(&heapLayout);
47479 }
47480
47481 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
47482 ma_bpf2_config bpf2Config;
47483 double q;
47484
47485 /* TODO: Calculate Q to make this a proper Butterworth filter. */
47486 q = 0.707107;
47487
47488 bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
47489
47490 if (isNew) {
47491 size_t bpf2HeapSizeInBytes;
47492
47493 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
47494 if (result == MA_SUCCESS) {
47495 result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]);
47496 }
47497 } else {
47498 result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]);
47499 }
47500
47501 if (result != MA_SUCCESS) {
47502 return result;
47503 }
47504 }
47505
47506 pBPF->bpf2Count = bpf2Count;
47507 pBPF->format = pConfig->format;
47508 pBPF->channels = pConfig->channels;
47509
47510 return MA_SUCCESS;
47511 }
47512
47513
47514 MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes)
47515 {
47516 ma_result result;
47517 ma_bpf_heap_layout heapLayout;
47518
47519 if (pHeapSizeInBytes == NULL) {
47520 return MA_INVALID_ARGS;
47521 }
47522
47523 *pHeapSizeInBytes = 0;
47524
47525 result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
47526 if (result != MA_SUCCESS) {
47527 return result;
47528 }
47529
47530 *pHeapSizeInBytes = heapLayout.sizeInBytes;
47531
47532 return MA_SUCCESS;
47533 }
47534
47535 MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF)
47536 {
47537 if (pBPF == NULL) {
47538 return MA_INVALID_ARGS;
47539 }
47540
47541 MA_ZERO_OBJECT(pBPF);
47542
47543 return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE);
47544 }
47545
47546 MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF)
47547 {
47548 ma_result result;
47549 size_t heapSizeInBytes;
47550 void* pHeap;
47551
47552 result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes);
47553 if (result != MA_SUCCESS) {
47554 return result;
47555 }
47556
47557 if (heapSizeInBytes > 0) {
47558 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47559 if (pHeap == NULL) {
47560 return MA_OUT_OF_MEMORY;
47561 }
47562 } else {
47563 pHeap = NULL;
47564 }
47565
47566 result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF);
47567 if (result != MA_SUCCESS) {
47568 ma_free(pHeap, pAllocationCallbacks);
47569 return result;
47570 }
47571
47572 pBPF->_ownsHeap = MA_TRUE;
47573 return MA_SUCCESS;
47574 }
47575
47576 MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
47577 {
47578 ma_uint32 ibpf2;
47579
47580 if (pBPF == NULL) {
47581 return;
47582 }
47583
47584 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47585 ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks);
47586 }
47587
47588 if (pBPF->_ownsHeap) {
47589 ma_free(pBPF->_pHeap, pAllocationCallbacks);
47590 }
47591 }
47592
47593 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
47594 {
47595 return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE);
47596 }
47597
47598 MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47599 {
47600 ma_result result;
47601 ma_uint32 ibpf2;
47602
47603 if (pBPF == NULL) {
47604 return MA_INVALID_ARGS;
47605 }
47606
47607 /* Faster path for in-place. */
47608 if (pFramesOut == pFramesIn) {
47609 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47610 result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount);
47611 if (result != MA_SUCCESS) {
47612 return result;
47613 }
47614 }
47615 }
47616
47617 /* Slightly slower path for copying. */
47618 if (pFramesOut != pFramesIn) {
47619 ma_uint32 iFrame;
47620
47621 /* */ if (pBPF->format == ma_format_f32) {
47622 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
47623 const float* pFramesInF32 = (const float*)pFramesIn;
47624
47625 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47626 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
47627
47628 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47629 ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32);
47630 }
47631
47632 pFramesOutF32 += pBPF->channels;
47633 pFramesInF32 += pBPF->channels;
47634 }
47635 } else if (pBPF->format == ma_format_s16) {
47636 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
47637 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
47638
47639 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
47640 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
47641
47642 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
47643 ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16);
47644 }
47645
47646 pFramesOutS16 += pBPF->channels;
47647 pFramesInS16 += pBPF->channels;
47648 }
47649 } else {
47650 MA_ASSERT(MA_FALSE);
47651 return MA_INVALID_OPERATION; /* Should never hit this. */
47652 }
47653 }
47654
47655 return MA_SUCCESS;
47656 }
47657
47658 MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF)
47659 {
47660 if (pBPF == NULL) {
47661 return 0;
47662 }
47663
47664 return pBPF->bpf2Count*2;
47665 }
47666
47667
47668 /**************************************************************************************************************************************************************
47669
47670 Notching Filter
47671
47672 **************************************************************************************************************************************************************/
47673 MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
47674 {
47675 ma_notch2_config config;
47676
47677 MA_ZERO_OBJECT(&config);
47678 config.format = format;
47679 config.channels = channels;
47680 config.sampleRate = sampleRate;
47681 config.q = q;
47682 config.frequency = frequency;
47683
47684 if (config.q == 0) {
47685 config.q = 0.707107;
47686 }
47687
47688 return config;
47689 }
47690
47691
47692 static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
47693 {
47694 ma_biquad_config bqConfig;
47695 double q;
47696 double w;
47697 double s;
47698 double c;
47699 double a;
47700
47701 MA_ASSERT(pConfig != NULL);
47702
47703 q = pConfig->q;
47704 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
47705 s = ma_sind(w);
47706 c = ma_cosd(w);
47707 a = s / (2*q);
47708
47709 bqConfig.b0 = 1;
47710 bqConfig.b1 = -2 * c;
47711 bqConfig.b2 = 1;
47712 bqConfig.a0 = 1 + a;
47713 bqConfig.a1 = -2 * c;
47714 bqConfig.a2 = 1 - a;
47715
47716 bqConfig.format = pConfig->format;
47717 bqConfig.channels = pConfig->channels;
47718
47719 return bqConfig;
47720 }
47721
47722 MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes)
47723 {
47724 ma_biquad_config bqConfig;
47725 bqConfig = ma_notch2__get_biquad_config(pConfig);
47726
47727 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
47728 }
47729
47730 MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter)
47731 {
47732 ma_result result;
47733 ma_biquad_config bqConfig;
47734
47735 if (pFilter == NULL) {
47736 return MA_INVALID_ARGS;
47737 }
47738
47739 MA_ZERO_OBJECT(pFilter);
47740
47741 if (pConfig == NULL) {
47742 return MA_INVALID_ARGS;
47743 }
47744
47745 bqConfig = ma_notch2__get_biquad_config(pConfig);
47746 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
47747 if (result != MA_SUCCESS) {
47748 return result;
47749 }
47750
47751 return MA_SUCCESS;
47752 }
47753
47754 MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter)
47755 {
47756 ma_result result;
47757 size_t heapSizeInBytes;
47758 void* pHeap;
47759
47760 result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes);
47761 if (result != MA_SUCCESS) {
47762 return result;
47763 }
47764
47765 if (heapSizeInBytes > 0) {
47766 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47767 if (pHeap == NULL) {
47768 return MA_OUT_OF_MEMORY;
47769 }
47770 } else {
47771 pHeap = NULL;
47772 }
47773
47774 result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter);
47775 if (result != MA_SUCCESS) {
47776 ma_free(pHeap, pAllocationCallbacks);
47777 return result;
47778 }
47779
47780 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
47781 return MA_SUCCESS;
47782 }
47783
47784 MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
47785 {
47786 if (pFilter == NULL) {
47787 return;
47788 }
47789
47790 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
47791 }
47792
47793 MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter)
47794 {
47795 ma_result result;
47796 ma_biquad_config bqConfig;
47797
47798 if (pFilter == NULL || pConfig == NULL) {
47799 return MA_INVALID_ARGS;
47800 }
47801
47802 bqConfig = ma_notch2__get_biquad_config(pConfig);
47803 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
47804 if (result != MA_SUCCESS) {
47805 return result;
47806 }
47807
47808 return MA_SUCCESS;
47809 }
47810
47811 static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
47812 {
47813 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
47814 }
47815
47816 static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
47817 {
47818 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
47819 }
47820
47821 MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47822 {
47823 if (pFilter == NULL) {
47824 return MA_INVALID_ARGS;
47825 }
47826
47827 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
47828 }
47829
47830 MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter)
47831 {
47832 if (pFilter == NULL) {
47833 return 0;
47834 }
47835
47836 return ma_biquad_get_latency(&pFilter->bq);
47837 }
47838
47839
47840
47841 /**************************************************************************************************************************************************************
47842
47843 Peaking EQ Filter
47844
47845 **************************************************************************************************************************************************************/
47846 MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
47847 {
47848 ma_peak2_config config;
47849
47850 MA_ZERO_OBJECT(&config);
47851 config.format = format;
47852 config.channels = channels;
47853 config.sampleRate = sampleRate;
47854 config.gainDB = gainDB;
47855 config.q = q;
47856 config.frequency = frequency;
47857
47858 if (config.q == 0) {
47859 config.q = 0.707107;
47860 }
47861
47862 return config;
47863 }
47864
47865
47866 static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
47867 {
47868 ma_biquad_config bqConfig;
47869 double q;
47870 double w;
47871 double s;
47872 double c;
47873 double a;
47874 double A;
47875
47876 MA_ASSERT(pConfig != NULL);
47877
47878 q = pConfig->q;
47879 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
47880 s = ma_sind(w);
47881 c = ma_cosd(w);
47882 a = s / (2*q);
47883 A = ma_powd(10, (pConfig->gainDB / 40));
47884
47885 bqConfig.b0 = 1 + (a * A);
47886 bqConfig.b1 = -2 * c;
47887 bqConfig.b2 = 1 - (a * A);
47888 bqConfig.a0 = 1 + (a / A);
47889 bqConfig.a1 = -2 * c;
47890 bqConfig.a2 = 1 - (a / A);
47891
47892 bqConfig.format = pConfig->format;
47893 bqConfig.channels = pConfig->channels;
47894
47895 return bqConfig;
47896 }
47897
47898 MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes)
47899 {
47900 ma_biquad_config bqConfig;
47901 bqConfig = ma_peak2__get_biquad_config(pConfig);
47902
47903 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
47904 }
47905
47906 MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter)
47907 {
47908 ma_result result;
47909 ma_biquad_config bqConfig;
47910
47911 if (pFilter == NULL) {
47912 return MA_INVALID_ARGS;
47913 }
47914
47915 MA_ZERO_OBJECT(pFilter);
47916
47917 if (pConfig == NULL) {
47918 return MA_INVALID_ARGS;
47919 }
47920
47921 bqConfig = ma_peak2__get_biquad_config(pConfig);
47922 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
47923 if (result != MA_SUCCESS) {
47924 return result;
47925 }
47926
47927 return MA_SUCCESS;
47928 }
47929
47930 MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter)
47931 {
47932 ma_result result;
47933 size_t heapSizeInBytes;
47934 void* pHeap;
47935
47936 result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes);
47937 if (result != MA_SUCCESS) {
47938 return result;
47939 }
47940
47941 if (heapSizeInBytes > 0) {
47942 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47943 if (pHeap == NULL) {
47944 return MA_OUT_OF_MEMORY;
47945 }
47946 } else {
47947 pHeap = NULL;
47948 }
47949
47950 result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter);
47951 if (result != MA_SUCCESS) {
47952 ma_free(pHeap, pAllocationCallbacks);
47953 return result;
47954 }
47955
47956 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
47957 return MA_SUCCESS;
47958 }
47959
47960 MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
47961 {
47962 if (pFilter == NULL) {
47963 return;
47964 }
47965
47966 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
47967 }
47968
47969 MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)
47970 {
47971 ma_result result;
47972 ma_biquad_config bqConfig;
47973
47974 if (pFilter == NULL || pConfig == NULL) {
47975 return MA_INVALID_ARGS;
47976 }
47977
47978 bqConfig = ma_peak2__get_biquad_config(pConfig);
47979 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
47980 if (result != MA_SUCCESS) {
47981 return result;
47982 }
47983
47984 return MA_SUCCESS;
47985 }
47986
47987 static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
47988 {
47989 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
47990 }
47991
47992 static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
47993 {
47994 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
47995 }
47996
47997 MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47998 {
47999 if (pFilter == NULL) {
48000 return MA_INVALID_ARGS;
48001 }
48002
48003 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
48004 }
48005
48006 MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter)
48007 {
48008 if (pFilter == NULL) {
48009 return 0;
48010 }
48011
48012 return ma_biquad_get_latency(&pFilter->bq);
48013 }
48014
48015
48016 /**************************************************************************************************************************************************************
48017
48018 Low Shelf Filter
48019
48020 **************************************************************************************************************************************************************/
48021 MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
48022 {
48023 ma_loshelf2_config config;
48024
48025 MA_ZERO_OBJECT(&config);
48026 config.format = format;
48027 config.channels = channels;
48028 config.sampleRate = sampleRate;
48029 config.gainDB = gainDB;
48030 config.shelfSlope = shelfSlope;
48031 config.frequency = frequency;
48032
48033 return config;
48034 }
48035
48036
48037 static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
48038 {
48039 ma_biquad_config bqConfig;
48040 double w;
48041 double s;
48042 double c;
48043 double A;
48044 double S;
48045 double a;
48046 double sqrtA;
48047
48048 MA_ASSERT(pConfig != NULL);
48049
48050 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
48051 s = ma_sind(w);
48052 c = ma_cosd(w);
48053 A = ma_powd(10, (pConfig->gainDB / 40));
48054 S = pConfig->shelfSlope;
48055 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
48056 sqrtA = 2*ma_sqrtd(A)*a;
48057
48058 bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
48059 bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
48060 bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
48061 bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
48062 bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
48063 bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
48064
48065 bqConfig.format = pConfig->format;
48066 bqConfig.channels = pConfig->channels;
48067
48068 return bqConfig;
48069 }
48070
48071 MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes)
48072 {
48073 ma_biquad_config bqConfig;
48074 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
48075
48076 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
48077 }
48078
48079 MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter)
48080 {
48081 ma_result result;
48082 ma_biquad_config bqConfig;
48083
48084 if (pFilter == NULL) {
48085 return MA_INVALID_ARGS;
48086 }
48087
48088 MA_ZERO_OBJECT(pFilter);
48089
48090 if (pConfig == NULL) {
48091 return MA_INVALID_ARGS;
48092 }
48093
48094 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
48095 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
48096 if (result != MA_SUCCESS) {
48097 return result;
48098 }
48099
48100 return MA_SUCCESS;
48101 }
48102
48103 MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter)
48104 {
48105 ma_result result;
48106 size_t heapSizeInBytes;
48107 void* pHeap;
48108
48109 result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes);
48110 if (result != MA_SUCCESS) {
48111 return result;
48112 }
48113
48114 if (heapSizeInBytes > 0) {
48115 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48116 if (pHeap == NULL) {
48117 return MA_OUT_OF_MEMORY;
48118 }
48119 } else {
48120 pHeap = NULL;
48121 }
48122
48123 result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter);
48124 if (result != MA_SUCCESS) {
48125 ma_free(pHeap, pAllocationCallbacks);
48126 return result;
48127 }
48128
48129 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
48130 return MA_SUCCESS;
48131 }
48132
48133 MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
48134 {
48135 if (pFilter == NULL) {
48136 return;
48137 }
48138
48139 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
48140 }
48141
48142 MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
48143 {
48144 ma_result result;
48145 ma_biquad_config bqConfig;
48146
48147 if (pFilter == NULL || pConfig == NULL) {
48148 return MA_INVALID_ARGS;
48149 }
48150
48151 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
48152 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
48153 if (result != MA_SUCCESS) {
48154 return result;
48155 }
48156
48157 return MA_SUCCESS;
48158 }
48159
48160 static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
48161 {
48162 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
48163 }
48164
48165 static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
48166 {
48167 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
48168 }
48169
48170 MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48171 {
48172 if (pFilter == NULL) {
48173 return MA_INVALID_ARGS;
48174 }
48175
48176 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
48177 }
48178
48179 MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter)
48180 {
48181 if (pFilter == NULL) {
48182 return 0;
48183 }
48184
48185 return ma_biquad_get_latency(&pFilter->bq);
48186 }
48187
48188
48189 /**************************************************************************************************************************************************************
48190
48191 High Shelf Filter
48192
48193 **************************************************************************************************************************************************************/
48194 MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
48195 {
48196 ma_hishelf2_config config;
48197
48198 MA_ZERO_OBJECT(&config);
48199 config.format = format;
48200 config.channels = channels;
48201 config.sampleRate = sampleRate;
48202 config.gainDB = gainDB;
48203 config.shelfSlope = shelfSlope;
48204 config.frequency = frequency;
48205
48206 return config;
48207 }
48208
48209
48210 static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
48211 {
48212 ma_biquad_config bqConfig;
48213 double w;
48214 double s;
48215 double c;
48216 double A;
48217 double S;
48218 double a;
48219 double sqrtA;
48220
48221 MA_ASSERT(pConfig != NULL);
48222
48223 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
48224 s = ma_sind(w);
48225 c = ma_cosd(w);
48226 A = ma_powd(10, (pConfig->gainDB / 40));
48227 S = pConfig->shelfSlope;
48228 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
48229 sqrtA = 2*ma_sqrtd(A)*a;
48230
48231 bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
48232 bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
48233 bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
48234 bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
48235 bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
48236 bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
48237
48238 bqConfig.format = pConfig->format;
48239 bqConfig.channels = pConfig->channels;
48240
48241 return bqConfig;
48242 }
48243
48244 MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes)
48245 {
48246 ma_biquad_config bqConfig;
48247 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
48248
48249 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
48250 }
48251
48252 MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter)
48253 {
48254 ma_result result;
48255 ma_biquad_config bqConfig;
48256
48257 if (pFilter == NULL) {
48258 return MA_INVALID_ARGS;
48259 }
48260
48261 MA_ZERO_OBJECT(pFilter);
48262
48263 if (pConfig == NULL) {
48264 return MA_INVALID_ARGS;
48265 }
48266
48267 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
48268 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
48269 if (result != MA_SUCCESS) {
48270 return result;
48271 }
48272
48273 return MA_SUCCESS;
48274 }
48275
48276 MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter)
48277 {
48278 ma_result result;
48279 size_t heapSizeInBytes;
48280 void* pHeap;
48281
48282 result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes);
48283 if (result != MA_SUCCESS) {
48284 return result;
48285 }
48286
48287 if (heapSizeInBytes > 0) {
48288 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48289 if (pHeap == NULL) {
48290 return MA_OUT_OF_MEMORY;
48291 }
48292 } else {
48293 pHeap = NULL;
48294 }
48295
48296 result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter);
48297 if (result != MA_SUCCESS) {
48298 ma_free(pHeap, pAllocationCallbacks);
48299 return result;
48300 }
48301
48302 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
48303 return MA_SUCCESS;
48304 }
48305
48306 MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
48307 {
48308 if (pFilter == NULL) {
48309 return;
48310 }
48311
48312 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
48313 }
48314
48315 MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
48316 {
48317 ma_result result;
48318 ma_biquad_config bqConfig;
48319
48320 if (pFilter == NULL || pConfig == NULL) {
48321 return MA_INVALID_ARGS;
48322 }
48323
48324 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
48325 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
48326 if (result != MA_SUCCESS) {
48327 return result;
48328 }
48329
48330 return MA_SUCCESS;
48331 }
48332
48333 static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
48334 {
48335 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
48336 }
48337
48338 static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
48339 {
48340 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
48341 }
48342
48343 MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48344 {
48345 if (pFilter == NULL) {
48346 return MA_INVALID_ARGS;
48347 }
48348
48349 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
48350 }
48351
48352 MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter)
48353 {
48354 if (pFilter == NULL) {
48355 return 0;
48356 }
48357
48358 return ma_biquad_get_latency(&pFilter->bq);
48359 }
48360
48361
48362
48363 /*
48364 Delay
48365 */
48366 MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
48367 {
48368 ma_delay_config config;
48369
48370 MA_ZERO_OBJECT(&config);
48371 config.channels = channels;
48372 config.sampleRate = sampleRate;
48373 config.delayInFrames = delayInFrames;
48374 config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */
48375 config.wet = 1;
48376 config.dry = 1;
48377 config.decay = decay;
48378
48379 return config;
48380 }
48381
48382
48383 MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay)
48384 {
48385 if (pDelay == NULL) {
48386 return MA_INVALID_ARGS;
48387 }
48388
48389 MA_ZERO_OBJECT(pDelay);
48390
48391 if (pConfig == NULL) {
48392 return MA_INVALID_ARGS;
48393 }
48394
48395 if (pConfig->decay < 0 || pConfig->decay > 1) {
48396 return MA_INVALID_ARGS;
48397 }
48398
48399 pDelay->config = *pConfig;
48400 pDelay->bufferSizeInFrames = pConfig->delayInFrames;
48401 pDelay->cursor = 0;
48402
48403 pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks);
48404 if (pDelay->pBuffer == NULL) {
48405 return MA_OUT_OF_MEMORY;
48406 }
48407
48408 ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels);
48409
48410 return MA_SUCCESS;
48411 }
48412
48413 MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks)
48414 {
48415 if (pDelay == NULL) {
48416 return;
48417 }
48418
48419 ma_free(pDelay->pBuffer, pAllocationCallbacks);
48420 }
48421
48422 MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
48423 {
48424 ma_uint32 iFrame;
48425 ma_uint32 iChannel;
48426 float* pFramesOutF32 = (float*)pFramesOut;
48427 const float* pFramesInF32 = (const float*)pFramesIn;
48428
48429 if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) {
48430 return MA_INVALID_ARGS;
48431 }
48432
48433 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48434 for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) {
48435 ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel;
48436
48437 if (pDelay->config.delayStart) {
48438 /* Delayed start. */
48439
48440 /* Read */
48441 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
48442
48443 /* Feedback */
48444 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
48445 } else {
48446 /* Immediate start */
48447
48448 /* Feedback */
48449 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
48450
48451 /* Read */
48452 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
48453 }
48454 }
48455
48456 pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames;
48457
48458 pFramesOutF32 += pDelay->config.channels;
48459 pFramesInF32 += pDelay->config.channels;
48460 }
48461
48462 return MA_SUCCESS;
48463 }
48464
48465 MA_API void ma_delay_set_wet(ma_delay* pDelay, float value)
48466 {
48467 if (pDelay == NULL) {
48468 return;
48469 }
48470
48471 pDelay->config.wet = value;
48472 }
48473
48474 MA_API float ma_delay_get_wet(const ma_delay* pDelay)
48475 {
48476 if (pDelay == NULL) {
48477 return 0;
48478 }
48479
48480 return pDelay->config.wet;
48481 }
48482
48483 MA_API void ma_delay_set_dry(ma_delay* pDelay, float value)
48484 {
48485 if (pDelay == NULL) {
48486 return;
48487 }
48488
48489 pDelay->config.dry = value;
48490 }
48491
48492 MA_API float ma_delay_get_dry(const ma_delay* pDelay)
48493 {
48494 if (pDelay == NULL) {
48495 return 0;
48496 }
48497
48498 return pDelay->config.dry;
48499 }
48500
48501 MA_API void ma_delay_set_decay(ma_delay* pDelay, float value)
48502 {
48503 if (pDelay == NULL) {
48504 return;
48505 }
48506
48507 pDelay->config.decay = value;
48508 }
48509
48510 MA_API float ma_delay_get_decay(const ma_delay* pDelay)
48511 {
48512 if (pDelay == NULL) {
48513 return 0;
48514 }
48515
48516 return pDelay->config.decay;
48517 }
48518
48519
48520 MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames)
48521 {
48522 ma_gainer_config config;
48523
48524 MA_ZERO_OBJECT(&config);
48525 config.channels = channels;
48526 config.smoothTimeInFrames = smoothTimeInFrames;
48527
48528 return config;
48529 }
48530
48531
48532 typedef struct
48533 {
48534 size_t sizeInBytes;
48535 size_t oldGainsOffset;
48536 size_t newGainsOffset;
48537 } ma_gainer_heap_layout;
48538
48539 static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout)
48540 {
48541 MA_ASSERT(pHeapLayout != NULL);
48542
48543 MA_ZERO_OBJECT(pHeapLayout);
48544
48545 if (pConfig == NULL) {
48546 return MA_INVALID_ARGS;
48547 }
48548
48549 if (pConfig->channels == 0) {
48550 return MA_INVALID_ARGS;
48551 }
48552
48553 pHeapLayout->sizeInBytes = 0;
48554
48555 /* Old gains. */
48556 pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes;
48557 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
48558
48559 /* New gains. */
48560 pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes;
48561 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
48562
48563 /* Alignment. */
48564 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
48565
48566 return MA_SUCCESS;
48567 }
48568
48569
48570 MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes)
48571 {
48572 ma_result result;
48573 ma_gainer_heap_layout heapLayout;
48574
48575 if (pHeapSizeInBytes == NULL) {
48576 return MA_INVALID_ARGS;
48577 }
48578
48579 *pHeapSizeInBytes = 0;
48580
48581 result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
48582 if (result != MA_SUCCESS) {
48583 return MA_INVALID_ARGS;
48584 }
48585
48586 *pHeapSizeInBytes = heapLayout.sizeInBytes;
48587
48588 return MA_SUCCESS;
48589 }
48590
48591
48592 MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer)
48593 {
48594 ma_result result;
48595 ma_gainer_heap_layout heapLayout;
48596 ma_uint32 iChannel;
48597
48598 if (pGainer == NULL) {
48599 return MA_INVALID_ARGS;
48600 }
48601
48602 MA_ZERO_OBJECT(pGainer);
48603
48604 if (pConfig == NULL || pHeap == NULL) {
48605 return MA_INVALID_ARGS;
48606 }
48607
48608 result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
48609 if (result != MA_SUCCESS) {
48610 return result;
48611 }
48612
48613 pGainer->_pHeap = pHeap;
48614 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
48615
48616 pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset);
48617 pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset);
48618 pGainer->masterVolume = 1;
48619
48620 pGainer->config = *pConfig;
48621 pGainer->t = (ma_uint32)-1; /* No interpolation by default. */
48622
48623 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
48624 pGainer->pOldGains[iChannel] = 1;
48625 pGainer->pNewGains[iChannel] = 1;
48626 }
48627
48628 return MA_SUCCESS;
48629 }
48630
48631 MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer)
48632 {
48633 ma_result result;
48634 size_t heapSizeInBytes;
48635 void* pHeap;
48636
48637 result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes);
48638 if (result != MA_SUCCESS) {
48639 return result; /* Failed to retrieve the size of the heap allocation. */
48640 }
48641
48642 if (heapSizeInBytes > 0) {
48643 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48644 if (pHeap == NULL) {
48645 return MA_OUT_OF_MEMORY;
48646 }
48647 } else {
48648 pHeap = NULL;
48649 }
48650
48651 result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer);
48652 if (result != MA_SUCCESS) {
48653 ma_free(pHeap, pAllocationCallbacks);
48654 return result;
48655 }
48656
48657 pGainer->_ownsHeap = MA_TRUE;
48658 return MA_SUCCESS;
48659 }
48660
48661 MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks)
48662 {
48663 if (pGainer == NULL) {
48664 return;
48665 }
48666
48667 if (pGainer->_ownsHeap) {
48668 ma_free(pGainer->_pHeap, pAllocationCallbacks);
48669 }
48670 }
48671
48672 static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel)
48673 {
48674 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
48675 return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a);
48676 }
48677
48678 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)
48679 {
48680 ma_uint64 iFrame;
48681 ma_uint32 iChannel;
48682 ma_uint64 interpolatedFrameCount;
48683
48684 MA_ASSERT(pGainer != NULL);
48685
48686 /*
48687 We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When
48688 linear interpolation is not needed we can do a simple volume adjustment which will be more
48689 efficient than a lerp with an alpha value of 1.
48690
48691 To do this, all we need to do is determine how many frames need to have a lerp applied. Then we
48692 just process that number of frames with linear interpolation. After that we run on an optimized
48693 path which just applies the new gains without a lerp.
48694 */
48695 if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
48696 interpolatedFrameCount = 0;
48697 } else {
48698 interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames;
48699 if (interpolatedFrameCount > frameCount) {
48700 interpolatedFrameCount = frameCount;
48701 }
48702 }
48703
48704 /*
48705 Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers
48706 so that the fast path can work naturally without consideration of the interpolated path.
48707 */
48708 if (interpolatedFrameCount > 0) {
48709 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
48710 if (pFramesOut != NULL && pFramesIn != NULL) {
48711 /*
48712 All we're really doing here is moving the old gains towards the new gains. We don't want to
48713 be modifying the gains inside the ma_gainer object because that will break things. Instead
48714 we can make a copy here on the stack. For extreme channel counts we can fall back to a slower
48715 implementation which just uses a standard lerp.
48716 */
48717 float* pFramesOutF32 = (float*)pFramesOut;
48718 const float* pFramesInF32 = (const float*)pFramesIn;
48719 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
48720 float d = 1.0f / pGainer->config.smoothTimeInFrames;
48721
48722 if (pGainer->config.channels <= 32) {
48723 float pRunningGain[32];
48724 float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */
48725
48726 /* Initialize the running gain. */
48727 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48728 float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume;
48729 pRunningGainDelta[iChannel] = t * d;
48730 pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a);
48731 }
48732
48733 iFrame = 0;
48734
48735 /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */
48736 if (pGainer->config.channels == 2) {
48737 #if defined(MA_SUPPORT_SSE2)
48738 if (ma_has_sse2()) {
48739 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
48740
48741 /* Expand some arrays so we can have a clean SIMD loop below. */
48742 __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]);
48743 __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]);
48744
48745 for (; iFrame < unrolledLoopCount; iFrame += 1) {
48746 _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0));
48747 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
48748 }
48749
48750 iFrame = unrolledLoopCount << 1;
48751 } else
48752 #endif
48753 {
48754 /*
48755 Two different scalar implementations here. Clang (and I assume GCC) will vectorize
48756 both of these, but the bottom version results in a nicer vectorization with less
48757 instructions emitted. The problem, however, is that the bottom version runs slower
48758 when compiled with MSVC. The top version will be partially vectorized by MSVC.
48759 */
48760 #if defined(_MSC_VER) && !defined(__clang__)
48761 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
48762
48763 /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */
48764 pRunningGainDelta[2] = pRunningGainDelta[0];
48765 pRunningGainDelta[3] = pRunningGainDelta[1];
48766 pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0];
48767 pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1];
48768
48769 for (; iFrame < unrolledLoopCount; iFrame += 1) {
48770 pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0];
48771 pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1];
48772 pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2];
48773 pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3];
48774
48775 /* Move the running gain forward towards the new gain. */
48776 pRunningGain[0] += pRunningGainDelta[0];
48777 pRunningGain[1] += pRunningGainDelta[1];
48778 pRunningGain[2] += pRunningGainDelta[2];
48779 pRunningGain[3] += pRunningGainDelta[3];
48780 }
48781
48782 iFrame = unrolledLoopCount << 1;
48783 #else
48784 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48785 for (iChannel = 0; iChannel < 2; iChannel += 1) {
48786 pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel];
48787 }
48788
48789 for (iChannel = 0; iChannel < 2; iChannel += 1) {
48790 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48791 }
48792 }
48793 #endif
48794 }
48795 } else if (pGainer->config.channels == 6) {
48796 #if defined(MA_SUPPORT_SSE2)
48797 if (ma_has_sse2()) {
48798 /*
48799 For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames
48800 at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays
48801 so we can do clean 4x SIMD operations.
48802 */
48803 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
48804
48805 /* Expand some arrays so we can have a clean SIMD loop below. */
48806 __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]);
48807 __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]);
48808 __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]);
48809
48810 __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]);
48811 __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]);
48812 __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]);
48813
48814 for (; iFrame < unrolledLoopCount; iFrame += 1) {
48815 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0));
48816 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1));
48817 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2));
48818
48819 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
48820 runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
48821 runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2);
48822 }
48823
48824 iFrame = unrolledLoopCount << 1;
48825 } else
48826 #endif
48827 {
48828 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48829 for (iChannel = 0; iChannel < 6; iChannel += 1) {
48830 pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel];
48831 }
48832
48833 /* Move the running gain forward towards the new gain. */
48834 for (iChannel = 0; iChannel < 6; iChannel += 1) {
48835 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48836 }
48837 }
48838 }
48839 } else if (pGainer->config.channels == 8) {
48840 /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */
48841 #if defined(MA_SUPPORT_SSE2)
48842 if (ma_has_sse2()) {
48843 __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]);
48844 __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]);
48845 __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]);
48846 __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]);
48847
48848 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48849 _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0));
48850 _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1));
48851
48852 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
48853 runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
48854 }
48855 } else
48856 #endif
48857 {
48858 /* This is crafted so that it auto-vectorizes when compiled with Clang. */
48859 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48860 for (iChannel = 0; iChannel < 8; iChannel += 1) {
48861 pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel];
48862 }
48863
48864 /* Move the running gain forward towards the new gain. */
48865 for (iChannel = 0; iChannel < 8; iChannel += 1) {
48866 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48867 }
48868 }
48869 }
48870 }
48871
48872 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
48873 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48874 pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel];
48875 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
48876 }
48877 }
48878 } else {
48879 /* 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. */
48880 for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) {
48881 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48882 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;
48883 }
48884
48885 a += d;
48886 }
48887 }
48888 }
48889
48890 /* Make sure the timer is updated. */
48891 pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames);
48892
48893 /* Adjust our arguments so the next part can work normally. */
48894 frameCount -= interpolatedFrameCount;
48895 pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float));
48896 pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float));
48897 }
48898
48899 /* All we need to do here is apply the new gains using an optimized path. */
48900 if (pFramesOut != NULL && pFramesIn != NULL) {
48901 if (pGainer->config.channels <= 32) {
48902 float gains[32];
48903 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48904 gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume;
48905 }
48906
48907 ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains);
48908 } else {
48909 /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */
48910 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48911 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48912 ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume;
48913 }
48914 }
48915 }
48916 }
48917
48918 /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
48919 if (pGainer->t == (ma_uint32)-1) {
48920 pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount);
48921 }
48922
48923 #if 0
48924 if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
48925 /* Fast path. No gain calculation required. */
48926 ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);
48927 ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume);
48928
48929 /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
48930 if (pGainer->t == (ma_uint32)-1) {
48931 pGainer->t = pGainer->config.smoothTimeInFrames;
48932 }
48933 } else {
48934 /* Slow path. Need to interpolate the gain for each channel individually. */
48935
48936 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
48937 if (pFramesOut != NULL && pFramesIn != NULL) {
48938 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
48939 float d = 1.0f / pGainer->config.smoothTimeInFrames;
48940 ma_uint32 channelCount = pGainer->config.channels;
48941
48942 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48943 for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
48944 pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume;
48945 }
48946
48947 pFramesOutF32 += channelCount;
48948 pFramesInF32 += channelCount;
48949
48950 a += d;
48951 if (a > 1) {
48952 a = 1;
48953 }
48954 }
48955 }
48956
48957 pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);
48958
48959 #if 0 /* Reference implementation. */
48960 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48961 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
48962 if (pFramesOut != NULL && pFramesIn != NULL) {
48963 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
48964 pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume;
48965 }
48966 }
48967
48968 /* Move interpolation time forward, but don't go beyond our smoothing time. */
48969 pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
48970 }
48971 #endif
48972 }
48973 #endif
48974
48975 return MA_SUCCESS;
48976 }
48977
48978 MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48979 {
48980 if (pGainer == NULL) {
48981 return MA_INVALID_ARGS;
48982 }
48983
48984 /*
48985 ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which
48986 helps with auto-vectorization.
48987 */
48988 return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount);
48989 }
48990
48991 static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel)
48992 {
48993 pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel);
48994 pGainer->pNewGains[iChannel] = newGain;
48995 }
48996
48997 static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer)
48998 {
48999 if (pGainer->t == (ma_uint32)-1) {
49000 pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */
49001 } else {
49002 pGainer->t = 0;
49003 }
49004 }
49005
49006 MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain)
49007 {
49008 ma_uint32 iChannel;
49009
49010 if (pGainer == NULL) {
49011 return MA_INVALID_ARGS;
49012 }
49013
49014 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
49015 ma_gainer_set_gain_by_index(pGainer, newGain, iChannel);
49016 }
49017
49018 /* 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. */
49019 ma_gainer_reset_smoothing_time(pGainer);
49020
49021 return MA_SUCCESS;
49022 }
49023
49024 MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains)
49025 {
49026 ma_uint32 iChannel;
49027
49028 if (pGainer == NULL || pNewGains == NULL) {
49029 return MA_INVALID_ARGS;
49030 }
49031
49032 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
49033 ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel);
49034 }
49035
49036 /* 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. */
49037 ma_gainer_reset_smoothing_time(pGainer);
49038
49039 return MA_SUCCESS;
49040 }
49041
49042 MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume)
49043 {
49044 if (pGainer == NULL) {
49045 return MA_INVALID_ARGS;
49046 }
49047
49048 pGainer->masterVolume = volume;
49049
49050 return MA_SUCCESS;
49051 }
49052
49053 MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume)
49054 {
49055 if (pGainer == NULL || pVolume == NULL) {
49056 return MA_INVALID_ARGS;
49057 }
49058
49059 *pVolume = pGainer->masterVolume;
49060
49061 return MA_SUCCESS;
49062 }
49063
49064
49065 MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels)
49066 {
49067 ma_panner_config config;
49068
49069 MA_ZERO_OBJECT(&config);
49070 config.format = format;
49071 config.channels = channels;
49072 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. */
49073 config.pan = 0;
49074
49075 return config;
49076 }
49077
49078
49079 MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner)
49080 {
49081 if (pPanner == NULL) {
49082 return MA_INVALID_ARGS;
49083 }
49084
49085 MA_ZERO_OBJECT(pPanner);
49086
49087 if (pConfig == NULL) {
49088 return MA_INVALID_ARGS;
49089 }
49090
49091 pPanner->format = pConfig->format;
49092 pPanner->channels = pConfig->channels;
49093 pPanner->mode = pConfig->mode;
49094 pPanner->pan = pConfig->pan;
49095
49096 return MA_SUCCESS;
49097 }
49098
49099 static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
49100 {
49101 ma_uint64 iFrame;
49102
49103 if (pan > 0) {
49104 float factor = 1.0f - pan;
49105 if (pFramesOut == pFramesIn) {
49106 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49107 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
49108 }
49109 } else {
49110 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49111 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
49112 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1];
49113 }
49114 }
49115 } else {
49116 float factor = 1.0f + pan;
49117 if (pFramesOut == pFramesIn) {
49118 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49119 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
49120 }
49121 } else {
49122 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49123 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0];
49124 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
49125 }
49126 }
49127 }
49128 }
49129
49130 static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
49131 {
49132 if (pan == 0) {
49133 /* Fast path. No panning required. */
49134 if (pFramesOut == pFramesIn) {
49135 /* No-op */
49136 } else {
49137 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49138 }
49139
49140 return;
49141 }
49142
49143 switch (format) {
49144 case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
49145
49146 /* Unknown format. Just copy. */
49147 default:
49148 {
49149 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49150 } break;
49151 }
49152 }
49153
49154
49155 static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
49156 {
49157 ma_uint64 iFrame;
49158
49159 if (pan > 0) {
49160 float factorL0 = 1.0f - pan;
49161 float factorL1 = 0.0f + pan;
49162
49163 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49164 float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0);
49165 float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1];
49166
49167 pFramesOut[iFrame*2 + 0] = sample0;
49168 pFramesOut[iFrame*2 + 1] = sample1;
49169 }
49170 } else {
49171 float factorR0 = 0.0f - pan;
49172 float factorR1 = 1.0f + pan;
49173
49174 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49175 float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0);
49176 float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1);
49177
49178 pFramesOut[iFrame*2 + 0] = sample0;
49179 pFramesOut[iFrame*2 + 1] = sample1;
49180 }
49181 }
49182 }
49183
49184 static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
49185 {
49186 if (pan == 0) {
49187 /* Fast path. No panning required. */
49188 if (pFramesOut == pFramesIn) {
49189 /* No-op */
49190 } else {
49191 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49192 }
49193
49194 return;
49195 }
49196
49197 switch (format) {
49198 case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
49199
49200 /* Unknown format. Just copy. */
49201 default:
49202 {
49203 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
49204 } break;
49205 }
49206 }
49207
49208 MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49209 {
49210 if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) {
49211 return MA_INVALID_ARGS;
49212 }
49213
49214 if (pPanner->channels == 2) {
49215 /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */
49216 if (pPanner->mode == ma_pan_mode_balance) {
49217 ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
49218 } else {
49219 ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
49220 }
49221 } else {
49222 if (pPanner->channels == 1) {
49223 /* Panning has no effect on mono streams. */
49224 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
49225 } else {
49226 /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */
49227 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
49228 }
49229 }
49230
49231 return MA_SUCCESS;
49232 }
49233
49234 MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode)
49235 {
49236 if (pPanner == NULL) {
49237 return;
49238 }
49239
49240 pPanner->mode = mode;
49241 }
49242
49243 MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner)
49244 {
49245 if (pPanner == NULL) {
49246 return ma_pan_mode_balance;
49247 }
49248
49249 return pPanner->mode;
49250 }
49251
49252 MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan)
49253 {
49254 if (pPanner == NULL) {
49255 return;
49256 }
49257
49258 pPanner->pan = ma_clamp(pan, -1.0f, 1.0f);
49259 }
49260
49261 MA_API float ma_panner_get_pan(const ma_panner* pPanner)
49262 {
49263 if (pPanner == NULL) {
49264 return 0;
49265 }
49266
49267 return pPanner->pan;
49268 }
49269
49270
49271
49272
49273 MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
49274 {
49275 ma_fader_config config;
49276
49277 MA_ZERO_OBJECT(&config);
49278 config.format = format;
49279 config.channels = channels;
49280 config.sampleRate = sampleRate;
49281
49282 return config;
49283 }
49284
49285
49286 MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader)
49287 {
49288 if (pFader == NULL) {
49289 return MA_INVALID_ARGS;
49290 }
49291
49292 MA_ZERO_OBJECT(pFader);
49293
49294 if (pConfig == NULL) {
49295 return MA_INVALID_ARGS;
49296 }
49297
49298 /* Only f32 is supported for now. */
49299 if (pConfig->format != ma_format_f32) {
49300 return MA_INVALID_ARGS;
49301 }
49302
49303 pFader->config = *pConfig;
49304 pFader->volumeBeg = 1;
49305 pFader->volumeEnd = 1;
49306 pFader->lengthInFrames = 0;
49307 pFader->cursorInFrames = 0;
49308
49309 return MA_SUCCESS;
49310 }
49311
49312 MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49313 {
49314 if (pFader == NULL) {
49315 return MA_INVALID_ARGS;
49316 }
49317
49318 /*
49319 For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for
49320 the conversion to a float which we use for the linear interpolation. This might be changed later.
49321 */
49322 if (frameCount + pFader->cursorInFrames > UINT_MAX) {
49323 frameCount = UINT_MAX - pFader->cursorInFrames;
49324 }
49325
49326 /* Optimized path if volumeBeg and volumeEnd are equal. */
49327 if (pFader->volumeBeg == pFader->volumeEnd) {
49328 if (pFader->volumeBeg == 1) {
49329 /* Straight copy. */
49330 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels);
49331 } else {
49332 /* Copy with volume. */
49333 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
49334 }
49335 } else {
49336 /* Slower path. Volumes are different, so may need to do an interpolation. */
49337 if (pFader->cursorInFrames >= pFader->lengthInFrames) {
49338 /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */
49339 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
49340 } else {
49341 /* Slow path. This is where we do the actual fading. */
49342 ma_uint64 iFrame;
49343 ma_uint32 iChannel;
49344
49345 /* For now we only support f32. Support for other formats will be added later. */
49346 if (pFader->config.format == ma_format_f32) {
49347 const float* pFramesInF32 = (const float*)pFramesIn;
49348 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
49349
49350 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49351 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. */
49352 float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a);
49353
49354 for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) {
49355 pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume;
49356 }
49357 }
49358 } else {
49359 return MA_NOT_IMPLEMENTED;
49360 }
49361 }
49362 }
49363
49364 pFader->cursorInFrames += frameCount;
49365
49366 return MA_SUCCESS;
49367 }
49368
49369 MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
49370 {
49371 if (pFader == NULL) {
49372 return;
49373 }
49374
49375 if (pFormat != NULL) {
49376 *pFormat = pFader->config.format;
49377 }
49378
49379 if (pChannels != NULL) {
49380 *pChannels = pFader->config.channels;
49381 }
49382
49383 if (pSampleRate != NULL) {
49384 *pSampleRate = pFader->config.sampleRate;
49385 }
49386 }
49387
49388 MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
49389 {
49390 if (pFader == NULL) {
49391 return;
49392 }
49393
49394 /* If the volume is negative, use current volume. */
49395 if (volumeBeg < 0) {
49396 volumeBeg = ma_fader_get_current_volume(pFader);
49397 }
49398
49399 /*
49400 The length needs to be clamped to 32-bits due to how we convert it to a float for linear
49401 interpolation reasons. I might change this requirement later, but for now it's not important.
49402 */
49403 if (lengthInFrames > UINT_MAX) {
49404 lengthInFrames = UINT_MAX;
49405 }
49406
49407 pFader->volumeBeg = volumeBeg;
49408 pFader->volumeEnd = volumeEnd;
49409 pFader->lengthInFrames = lengthInFrames;
49410 pFader->cursorInFrames = 0; /* Reset cursor. */
49411 }
49412
49413 MA_API float ma_fader_get_current_volume(const ma_fader* pFader)
49414 {
49415 if (pFader == NULL) {
49416 return 0.0f;
49417 }
49418
49419 /* The current volume depends on the position of the cursor. */
49420 if (pFader->cursorInFrames == 0) {
49421 return pFader->volumeBeg;
49422 } else if (pFader->cursorInFrames >= pFader->lengthInFrames) {
49423 return pFader->volumeEnd;
49424 } else {
49425 /* 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. */
49426 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(). */
49427 }
49428 }
49429
49430
49431
49432
49433
49434 MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z)
49435 {
49436 ma_vec3f v;
49437
49438 v.x = x;
49439 v.y = y;
49440 v.z = z;
49441
49442 return v;
49443 }
49444
49445 MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b)
49446 {
49447 return ma_vec3f_init_3f(
49448 a.x - b.x,
49449 a.y - b.y,
49450 a.z - b.z
49451 );
49452 }
49453
49454 MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a)
49455 {
49456 return ma_vec3f_init_3f(
49457 -a.x,
49458 -a.y,
49459 -a.z
49460 );
49461 }
49462
49463 MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b)
49464 {
49465 return a.x*b.x + a.y*b.y + a.z*b.z;
49466 }
49467
49468 MA_API float ma_vec3f_len2(ma_vec3f v)
49469 {
49470 return ma_vec3f_dot(v, v);
49471 }
49472
49473 MA_API float ma_vec3f_len(ma_vec3f v)
49474 {
49475 return (float)ma_sqrtd(ma_vec3f_len2(v));
49476 }
49477
49478
49479
49480 MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b)
49481 {
49482 return ma_vec3f_len(ma_vec3f_sub(a, b));
49483 }
49484
49485 MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v)
49486 {
49487 float invLen;
49488 float len2 = ma_vec3f_len2(v);
49489 if (len2 == 0) {
49490 return ma_vec3f_init_3f(0, 0, 0);
49491 }
49492
49493 invLen = ma_rsqrtf(len2);
49494 v.x *= invLen;
49495 v.y *= invLen;
49496 v.z *= invLen;
49497
49498 return v;
49499 }
49500
49501 MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b)
49502 {
49503 return ma_vec3f_init_3f(
49504 a.y*b.z - a.z*b.y,
49505 a.z*b.x - a.x*b.z,
49506 a.x*b.y - a.y*b.x
49507 );
49508 }
49509
49510
49511 MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value)
49512 {
49513 v->v = value;
49514 v->lock = 0; /* Important this is initialized to 0. */
49515 }
49516
49517 MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value)
49518 {
49519 ma_spinlock_lock(&v->lock);
49520 {
49521 v->v = value;
49522 }
49523 ma_spinlock_unlock(&v->lock);
49524 }
49525
49526 MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v)
49527 {
49528 ma_vec3f r;
49529
49530 ma_spinlock_lock(&v->lock);
49531 {
49532 r = v->v;
49533 }
49534 ma_spinlock_unlock(&v->lock);
49535
49536 return r;
49537 }
49538
49539
49540
49541 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);
49542 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition);
49543
49544
49545 #ifndef MA_DEFAULT_SPEED_OF_SOUND
49546 #define MA_DEFAULT_SPEED_OF_SOUND 343.3f
49547 #endif
49548
49549 /*
49550 These vectors represent the direction that speakers are facing from the center point. They're used
49551 for panning in the spatializer. Must be normalized.
49552 */
49553 static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = {
49554 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */
49555 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */
49556 {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */
49557 {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */
49558 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */
49559 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */
49560 {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */
49561 {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */
49562 {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */
49563 {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
49564 { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */
49565 {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */
49566 {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */
49567 { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */
49568 {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */
49569 { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */
49570 {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */
49571 {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */
49572 { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */
49573 {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */
49574 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */
49575 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */
49576 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */
49577 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */
49578 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */
49579 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */
49580 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */
49581 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */
49582 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */
49583 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */
49584 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */
49585 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */
49586 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */
49587 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */
49588 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */
49589 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */
49590 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */
49591 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */
49592 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */
49593 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */
49594 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */
49595 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */
49596 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */
49597 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */
49598 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */
49599 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */
49600 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */
49601 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */
49602 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */
49603 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */
49604 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */
49605 { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */
49606 };
49607
49608 static ma_vec3f ma_get_channel_direction(ma_channel channel)
49609 {
49610 if (channel >= MA_CHANNEL_POSITION_COUNT) {
49611 return ma_vec3f_init_3f(0, 0, -1);
49612 } else {
49613 return g_maChannelDirections[channel];
49614 }
49615 }
49616
49617
49618
49619 static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff)
49620 {
49621 if (minDistance >= maxDistance) {
49622 return 1; /* To avoid division by zero. Do not attenuate. */
49623 }
49624
49625 return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance));
49626 }
49627
49628 static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff)
49629 {
49630 if (minDistance >= maxDistance) {
49631 return 1; /* To avoid division by zero. Do not attenuate. */
49632 }
49633
49634 return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance);
49635 }
49636
49637 static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff)
49638 {
49639 if (minDistance >= maxDistance) {
49640 return 1; /* To avoid division by zero. Do not attenuate. */
49641 }
49642
49643 return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff);
49644 }
49645
49646
49647 /*
49648 Dopper Effect calculation taken from the OpenAL spec, with two main differences:
49649
49650 1) The source to listener vector will have already been calcualted at an earlier step so we can
49651 just use that directly. We need only the position of the source relative to the origin.
49652
49653 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight
49654 into the resampler directly.
49655 */
49656 static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor)
49657 {
49658 float len;
49659 float vls;
49660 float vss;
49661
49662 len = ma_vec3f_len(relativePosition);
49663
49664 /*
49665 There's a case where the position of the source will be right on top of the listener in which
49666 case the length will be 0 and we'll end up with a division by zero. We can just return a ratio
49667 of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary.
49668 */
49669 if (len == 0) {
49670 return 1.0;
49671 }
49672
49673 vls = ma_vec3f_dot(relativePosition, listenVelocity) / len;
49674 vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len;
49675
49676 vls = ma_min(vls, speedOfSound / dopplerFactor);
49677 vss = ma_min(vss, speedOfSound / dopplerFactor);
49678
49679 return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss);
49680 }
49681
49682
49683 static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount)
49684 {
49685 /*
49686 Special case for stereo. Want to default the left and right speakers to side left and side
49687 right so that they're facing directly down the X axis rather than slightly forward. Not
49688 doing this will result in sounds being quieter when behind the listener. This might
49689 actually be good for some scenerios, but I don't think it's an appropriate default because
49690 it can be a bit unexpected.
49691 */
49692 if (channelCount == 2) {
49693 pChannelMap[0] = MA_CHANNEL_SIDE_LEFT;
49694 pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT;
49695 } else {
49696 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
49697 }
49698 }
49699
49700
49701 MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut)
49702 {
49703 ma_spatializer_listener_config config;
49704
49705 MA_ZERO_OBJECT(&config);
49706 config.channelsOut = channelsOut;
49707 config.pChannelMapOut = NULL;
49708 config.handedness = ma_handedness_right;
49709 config.worldUp = ma_vec3f_init_3f(0, 1, 0);
49710 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
49711 config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */
49712 config.coneOuterGain = 0;
49713 config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */
49714
49715 return config;
49716 }
49717
49718
49719 typedef struct
49720 {
49721 size_t sizeInBytes;
49722 size_t channelMapOutOffset;
49723 } ma_spatializer_listener_heap_layout;
49724
49725 static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout)
49726 {
49727 MA_ASSERT(pHeapLayout != NULL);
49728
49729 MA_ZERO_OBJECT(pHeapLayout);
49730
49731 if (pConfig == NULL) {
49732 return MA_INVALID_ARGS;
49733 }
49734
49735 if (pConfig->channelsOut == 0) {
49736 return MA_INVALID_ARGS;
49737 }
49738
49739 pHeapLayout->sizeInBytes = 0;
49740
49741 /* Channel map. We always need this, even for passthroughs. */
49742 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
49743 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut);
49744
49745 return MA_SUCCESS;
49746 }
49747
49748
49749 MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes)
49750 {
49751 ma_result result;
49752 ma_spatializer_listener_heap_layout heapLayout;
49753
49754 if (pHeapSizeInBytes == NULL) {
49755 return MA_INVALID_ARGS;
49756 }
49757
49758 *pHeapSizeInBytes = 0;
49759
49760 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
49761 if (result != MA_SUCCESS) {
49762 return result;
49763 }
49764
49765 *pHeapSizeInBytes = heapLayout.sizeInBytes;
49766
49767 return MA_SUCCESS;
49768 }
49769
49770 MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener)
49771 {
49772 ma_result result;
49773 ma_spatializer_listener_heap_layout heapLayout;
49774
49775 if (pListener == NULL) {
49776 return MA_INVALID_ARGS;
49777 }
49778
49779 MA_ZERO_OBJECT(pListener);
49780
49781 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
49782 if (result != MA_SUCCESS) {
49783 return result;
49784 }
49785
49786 pListener->_pHeap = pHeap;
49787 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
49788
49789 pListener->config = *pConfig;
49790 ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0));
49791 ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1));
49792 ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0));
49793 pListener->isEnabled = MA_TRUE;
49794
49795 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
49796 if (pListener->config.handedness == ma_handedness_left) {
49797 ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener));
49798 ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z);
49799 }
49800
49801
49802 /* We must always have a valid channel map. */
49803 pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
49804
49805 /* Use a slightly different default channel map for stereo. */
49806 if (pConfig->pChannelMapOut == NULL) {
49807 ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut);
49808 } else {
49809 ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
49810 }
49811
49812 return MA_SUCCESS;
49813 }
49814
49815 MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener)
49816 {
49817 ma_result result;
49818 size_t heapSizeInBytes;
49819 void* pHeap;
49820
49821 result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes);
49822 if (result != MA_SUCCESS) {
49823 return result;
49824 }
49825
49826 if (heapSizeInBytes > 0) {
49827 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49828 if (pHeap == NULL) {
49829 return MA_OUT_OF_MEMORY;
49830 }
49831 } else {
49832 pHeap = NULL;
49833 }
49834
49835 result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener);
49836 if (result != MA_SUCCESS) {
49837 ma_free(pHeap, pAllocationCallbacks);
49838 return result;
49839 }
49840
49841 pListener->_ownsHeap = MA_TRUE;
49842 return MA_SUCCESS;
49843 }
49844
49845 MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks)
49846 {
49847 if (pListener == NULL) {
49848 return;
49849 }
49850
49851 if (pListener->_ownsHeap) {
49852 ma_free(pListener->_pHeap, pAllocationCallbacks);
49853 }
49854 }
49855
49856 MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener)
49857 {
49858 if (pListener == NULL) {
49859 return NULL;
49860 }
49861
49862 return pListener->config.pChannelMapOut;
49863 }
49864
49865 MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
49866 {
49867 if (pListener == NULL) {
49868 return;
49869 }
49870
49871 pListener->config.coneInnerAngleInRadians = innerAngleInRadians;
49872 pListener->config.coneOuterAngleInRadians = outerAngleInRadians;
49873 pListener->config.coneOuterGain = outerGain;
49874 }
49875
49876 MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
49877 {
49878 if (pListener == NULL) {
49879 return;
49880 }
49881
49882 if (pInnerAngleInRadians != NULL) {
49883 *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians;
49884 }
49885
49886 if (pOuterAngleInRadians != NULL) {
49887 *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians;
49888 }
49889
49890 if (pOuterGain != NULL) {
49891 *pOuterGain = pListener->config.coneOuterGain;
49892 }
49893 }
49894
49895 MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z)
49896 {
49897 if (pListener == NULL) {
49898 return;
49899 }
49900
49901 ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z));
49902 }
49903
49904 MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener)
49905 {
49906 if (pListener == NULL) {
49907 return ma_vec3f_init_3f(0, 0, 0);
49908 }
49909
49910 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. */
49911 }
49912
49913 MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z)
49914 {
49915 if (pListener == NULL) {
49916 return;
49917 }
49918
49919 ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z));
49920 }
49921
49922 MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener)
49923 {
49924 if (pListener == NULL) {
49925 return ma_vec3f_init_3f(0, 0, -1);
49926 }
49927
49928 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. */
49929 }
49930
49931 MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z)
49932 {
49933 if (pListener == NULL) {
49934 return;
49935 }
49936
49937 ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z));
49938 }
49939
49940 MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener)
49941 {
49942 if (pListener == NULL) {
49943 return ma_vec3f_init_3f(0, 0, 0);
49944 }
49945
49946 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. */
49947 }
49948
49949 MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound)
49950 {
49951 if (pListener == NULL) {
49952 return;
49953 }
49954
49955 pListener->config.speedOfSound = speedOfSound;
49956 }
49957
49958 MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener)
49959 {
49960 if (pListener == NULL) {
49961 return 0;
49962 }
49963
49964 return pListener->config.speedOfSound;
49965 }
49966
49967 MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z)
49968 {
49969 if (pListener == NULL) {
49970 return;
49971 }
49972
49973 pListener->config.worldUp = ma_vec3f_init_3f(x, y, z);
49974 }
49975
49976 MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener)
49977 {
49978 if (pListener == NULL) {
49979 return ma_vec3f_init_3f(0, 1, 0);
49980 }
49981
49982 return pListener->config.worldUp;
49983 }
49984
49985 MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled)
49986 {
49987 if (pListener == NULL) {
49988 return;
49989 }
49990
49991 pListener->isEnabled = isEnabled;
49992 }
49993
49994 MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener)
49995 {
49996 if (pListener == NULL) {
49997 return MA_FALSE;
49998 }
49999
50000 return pListener->isEnabled;
50001 }
50002
50003
50004
50005
50006 MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)
50007 {
50008 ma_spatializer_config config;
50009
50010 MA_ZERO_OBJECT(&config);
50011 config.channelsIn = channelsIn;
50012 config.channelsOut = channelsOut;
50013 config.pChannelMapIn = NULL;
50014 config.attenuationModel = ma_attenuation_model_inverse;
50015 config.positioning = ma_positioning_absolute;
50016 config.handedness = ma_handedness_right;
50017 config.minGain = 0;
50018 config.maxGain = 1;
50019 config.minDistance = 1;
50020 config.maxDistance = MA_FLT_MAX;
50021 config.rolloff = 1;
50022 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
50023 config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */
50024 config.coneOuterGain = 0.0f;
50025 config.dopplerFactor = 1;
50026 config.directionalAttenuationFactor = 1;
50027 config.minSpatializationChannelGain = 0.2f;
50028 config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */
50029
50030 return config;
50031 }
50032
50033
50034 static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)
50035 {
50036 MA_ASSERT(pConfig != NULL);
50037 return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);
50038 }
50039
50040 static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)
50041 {
50042 MA_ASSERT(pConfig != NULL);
50043
50044 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
50045 return MA_INVALID_ARGS;
50046 }
50047
50048 return MA_SUCCESS;
50049 }
50050
50051 typedef struct
50052 {
50053 size_t sizeInBytes;
50054 size_t channelMapInOffset;
50055 size_t newChannelGainsOffset;
50056 size_t gainerOffset;
50057 } ma_spatializer_heap_layout;
50058
50059 static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)
50060 {
50061 ma_result result;
50062
50063 MA_ASSERT(pHeapLayout != NULL);
50064
50065 MA_ZERO_OBJECT(pHeapLayout);
50066
50067 if (pConfig == NULL) {
50068 return MA_INVALID_ARGS;
50069 }
50070
50071 result = ma_spatializer_validate_config(pConfig);
50072 if (result != MA_SUCCESS) {
50073 return result;
50074 }
50075
50076 pHeapLayout->sizeInBytes = 0;
50077
50078 /* Channel map. */
50079 pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */
50080 if (pConfig->pChannelMapIn != NULL) {
50081 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
50082 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);
50083 }
50084
50085 /* New channel gains for output. */
50086 pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;
50087 pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);
50088
50089 /* Gainer. */
50090 {
50091 size_t gainerHeapSizeInBytes;
50092 ma_gainer_config gainerConfig;
50093
50094 gainerConfig = ma_spatializer_gainer_config_init(pConfig);
50095
50096 result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);
50097 if (result != MA_SUCCESS) {
50098 return result;
50099 }
50100
50101 pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
50102 pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);
50103 }
50104
50105 return MA_SUCCESS;
50106 }
50107
50108 MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)
50109 {
50110 ma_result result;
50111 ma_spatializer_heap_layout heapLayout;
50112
50113 if (pHeapSizeInBytes == NULL) {
50114 return MA_INVALID_ARGS;
50115 }
50116
50117 *pHeapSizeInBytes = 0; /* Safety. */
50118
50119 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
50120 if (result != MA_SUCCESS) {
50121 return result;
50122 }
50123
50124 *pHeapSizeInBytes = heapLayout.sizeInBytes;
50125
50126 return MA_SUCCESS;
50127 }
50128
50129
50130 MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer)
50131 {
50132 ma_result result;
50133 ma_spatializer_heap_layout heapLayout;
50134 ma_gainer_config gainerConfig;
50135
50136 if (pSpatializer == NULL) {
50137 return MA_INVALID_ARGS;
50138 }
50139
50140 MA_ZERO_OBJECT(pSpatializer);
50141
50142 if (pConfig == NULL || pHeap == NULL) {
50143 return MA_INVALID_ARGS;
50144 }
50145
50146 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
50147 if (result != MA_SUCCESS) {
50148 return result;
50149 }
50150
50151 pSpatializer->_pHeap = pHeap;
50152 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
50153
50154 pSpatializer->channelsIn = pConfig->channelsIn;
50155 pSpatializer->channelsOut = pConfig->channelsOut;
50156 pSpatializer->attenuationModel = pConfig->attenuationModel;
50157 pSpatializer->positioning = pConfig->positioning;
50158 pSpatializer->handedness = pConfig->handedness;
50159 pSpatializer->minGain = pConfig->minGain;
50160 pSpatializer->maxGain = pConfig->maxGain;
50161 pSpatializer->minDistance = pConfig->minDistance;
50162 pSpatializer->maxDistance = pConfig->maxDistance;
50163 pSpatializer->rolloff = pConfig->rolloff;
50164 pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians;
50165 pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians;
50166 pSpatializer->coneOuterGain = pConfig->coneOuterGain;
50167 pSpatializer->dopplerFactor = pConfig->dopplerFactor;
50168 pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain;
50169 pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor;
50170 pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames;
50171 ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0));
50172 ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1));
50173 ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0));
50174 pSpatializer->dopplerPitch = 1;
50175
50176 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
50177 if (pSpatializer->handedness == ma_handedness_left) {
50178 ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer));
50179 ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z);
50180 }
50181
50182 /* Channel map. This will be on the heap. */
50183 if (pConfig->pChannelMapIn != NULL) {
50184 pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
50185 ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn);
50186 }
50187
50188 /* New channel gains for output channels. */
50189 pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset);
50190
50191 /* Gainer. */
50192 gainerConfig = ma_spatializer_gainer_config_init(pConfig);
50193
50194 result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer);
50195 if (result != MA_SUCCESS) {
50196 return result; /* Failed to initialize the gainer. */
50197 }
50198
50199 return MA_SUCCESS;
50200 }
50201
50202 MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer)
50203 {
50204 ma_result result;
50205 size_t heapSizeInBytes;
50206 void* pHeap;
50207
50208 /* We'll need a heap allocation to retrieve the size. */
50209 result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes);
50210 if (result != MA_SUCCESS) {
50211 return result;
50212 }
50213
50214 if (heapSizeInBytes > 0) {
50215 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
50216 if (pHeap == NULL) {
50217 return MA_OUT_OF_MEMORY;
50218 }
50219 } else {
50220 pHeap = NULL;
50221 }
50222
50223 result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer);
50224 if (result != MA_SUCCESS) {
50225 ma_free(pHeap, pAllocationCallbacks);
50226 return result;
50227 }
50228
50229 pSpatializer->_ownsHeap = MA_TRUE;
50230 return MA_SUCCESS;
50231 }
50232
50233 MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks)
50234 {
50235 if (pSpatializer == NULL) {
50236 return;
50237 }
50238
50239 ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks);
50240
50241 if (pSpatializer->_ownsHeap) {
50242 ma_free(pSpatializer->_pHeap, pAllocationCallbacks);
50243 }
50244 }
50245
50246 static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain)
50247 {
50248 /*
50249 Angular attenuation.
50250
50251 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
50252 this out for ourselves at the expense of possibly being inconsistent with other implementations.
50253
50254 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
50255 just need to get the direction from the source to the listener and then do a dot product against that and the
50256 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
50257 angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
50258 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
50259 */
50260 if (coneInnerAngleInRadians < 6.283185f) {
50261 float angularGain = 1;
50262 float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f);
50263 float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f);
50264 float d;
50265
50266 d = ma_vec3f_dot(dirA, dirB);
50267
50268 if (d > cutoffInner) {
50269 /* It's inside the inner angle. */
50270 angularGain = 1;
50271 } else {
50272 /* It's outside the inner angle. */
50273 if (d > cutoffOuter) {
50274 /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */
50275 angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter));
50276 } else {
50277 /* It's outside the outer angle. */
50278 angularGain = coneOuterGain;
50279 }
50280 }
50281
50282 /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/
50283 return angularGain;
50284 } else {
50285 /* Inner angle is 360 degrees so no need to do any attenuation. */
50286 return 1;
50287 }
50288 }
50289
50290 MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
50291 {
50292 ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn;
50293 ma_channel* pChannelMapOut = pListener->config.pChannelMapOut;
50294
50295 if (pSpatializer == NULL) {
50296 return MA_INVALID_ARGS;
50297 }
50298
50299 /* If we're not spatializing we need to run an optimized path. */
50300 if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) {
50301 if (ma_spatializer_listener_is_enabled(pListener)) {
50302 /* No attenuation is required, but we'll need to do some channel conversion. */
50303 if (pSpatializer->channelsIn == pSpatializer->channelsOut) {
50304 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn);
50305 } else {
50306 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. */
50307 }
50308 } else {
50309 /* The listener is disabled. Output silence. */
50310 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
50311 }
50312
50313 /*
50314 We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is
50315 the correct thinking so might need to review this later.
50316 */
50317 pSpatializer->dopplerPitch = 1;
50318 } else {
50319 /*
50320 Let's first determine which listener the sound is closest to. Need to keep in mind that we
50321 might not have a world or any listeners, in which case we just spatializer based on the
50322 listener being positioned at the origin (0, 0, 0).
50323 */
50324 ma_vec3f relativePosNormalized;
50325 ma_vec3f relativePos; /* The position relative to the listener. */
50326 ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */
50327 ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */
50328 float speedOfSound;
50329 float distance = 0;
50330 float gain = 1;
50331 ma_uint32 iChannel;
50332 const ma_uint32 channelsOut = pSpatializer->channelsOut;
50333 const ma_uint32 channelsIn = pSpatializer->channelsIn;
50334 float minDistance = ma_spatializer_get_min_distance(pSpatializer);
50335 float maxDistance = ma_spatializer_get_max_distance(pSpatializer);
50336 float rolloff = ma_spatializer_get_rolloff(pSpatializer);
50337 float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer);
50338
50339 /*
50340 We'll need the listener velocity for doppler pitch calculations. The speed of sound is
50341 defined by the listener, so we'll grab that here too.
50342 */
50343 if (pListener != NULL) {
50344 listenerVel = ma_spatializer_listener_get_velocity(pListener);
50345 speedOfSound = pListener->config.speedOfSound;
50346 } else {
50347 listenerVel = ma_vec3f_init_3f(0, 0, 0);
50348 speedOfSound = MA_DEFAULT_SPEED_OF_SOUND;
50349 }
50350
50351 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
50352 /* There's no listener or we're using relative positioning. */
50353 relativePos = ma_spatializer_get_position(pSpatializer);
50354 relativeDir = ma_spatializer_get_direction(pSpatializer);
50355 } else {
50356 /*
50357 We've found a listener and we're using absolute positioning. We need to transform the
50358 sound's position and direction so that it's relative to listener. Later on we'll use
50359 this for determining the factors to apply to each channel to apply the panning effect.
50360 */
50361 ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir);
50362 }
50363
50364 distance = ma_vec3f_len(relativePos);
50365
50366 /* We've gathered the data, so now we can apply some spatialization. */
50367 switch (ma_spatializer_get_attenuation_model(pSpatializer)) {
50368 case ma_attenuation_model_inverse:
50369 {
50370 gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff);
50371 } break;
50372 case ma_attenuation_model_linear:
50373 {
50374 gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff);
50375 } break;
50376 case ma_attenuation_model_exponential:
50377 {
50378 gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff);
50379 } break;
50380 case ma_attenuation_model_none:
50381 default:
50382 {
50383 gain = 1;
50384 } break;
50385 }
50386
50387 /* Normalize the position. */
50388 if (distance > 0.001f) {
50389 float distanceInv = 1/distance;
50390 relativePosNormalized = relativePos;
50391 relativePosNormalized.x *= distanceInv;
50392 relativePosNormalized.y *= distanceInv;
50393 relativePosNormalized.z *= distanceInv;
50394 } else {
50395 distance = 0;
50396 relativePosNormalized = ma_vec3f_init_3f(0, 0, 0);
50397 }
50398
50399 /*
50400 Angular attenuation.
50401
50402 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
50403 this out for ourselves at the expense of possibly being inconsistent with other implementations.
50404
50405 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
50406 just need to get the direction from the source to the listener and then do a dot product against that and the
50407 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
50408 angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
50409 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
50410 */
50411 if (distance > 0) {
50412 /* Source anglular gain. */
50413 float spatializerConeInnerAngle;
50414 float spatializerConeOuterAngle;
50415 float spatializerConeOuterGain;
50416 ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain);
50417
50418 gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain);
50419
50420 /*
50421 We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that
50422 are positioned behind the listener. On default settings, this will have no effect.
50423 */
50424 if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) {
50425 ma_vec3f listenerDirection;
50426 float listenerInnerAngle;
50427 float listenerOuterAngle;
50428 float listenerOuterGain;
50429
50430 if (pListener->config.handedness == ma_handedness_right) {
50431 listenerDirection = ma_vec3f_init_3f(0, 0, -1);
50432 } else {
50433 listenerDirection = ma_vec3f_init_3f(0, 0, +1);
50434 }
50435
50436 listenerInnerAngle = pListener->config.coneInnerAngleInRadians;
50437 listenerOuterAngle = pListener->config.coneOuterAngleInRadians;
50438 listenerOuterGain = pListener->config.coneOuterGain;
50439
50440 gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain);
50441 }
50442 } else {
50443 /* The sound is right on top of the listener. Don't do any angular attenuation. */
50444 }
50445
50446
50447 /* Clamp the gain. */
50448 gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer));
50449
50450 /*
50451 The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel
50452 gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions
50453 to avoid harsh changes in gain.
50454 */
50455 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
50456 pSpatializer->pNewChannelGainsOut[iChannel] = gain;
50457 }
50458
50459 /*
50460 Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore
50461 the whole section of code here because we need to update some internal spatialization state.
50462 */
50463 if (ma_spatializer_listener_is_enabled(pListener)) {
50464 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);
50465 } else {
50466 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
50467 }
50468
50469
50470 /*
50471 Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for
50472 when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the
50473 gain to the final output.
50474 */
50475 /*printf("distance=%f; gain=%f\n", distance, gain);*/
50476
50477 /* We must have a valid channel map here to ensure we spatialize properly. */
50478 MA_ASSERT(pChannelMapOut != NULL);
50479
50480 /*
50481 We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being
50482 to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that
50483 the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and
50484 seeing how it goes. There might be better ways to do this.
50485
50486 To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a
50487 direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will
50488 be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized
50489 position of the sound.
50490 */
50491
50492 /*
50493 Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's
50494 relation to the direction of the channel.
50495 */
50496 if (distance > 0) {
50497 ma_vec3f unitPos = relativePos;
50498 float distanceInv = 1/distance;
50499 unitPos.x *= distanceInv;
50500 unitPos.y *= distanceInv;
50501 unitPos.z *= distanceInv;
50502
50503 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
50504 ma_channel channelOut;
50505 float d;
50506 float dMin;
50507
50508 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel);
50509 if (ma_is_spatial_channel_position(channelOut)) {
50510 d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer));
50511 } else {
50512 d = 1; /* It's not a spatial channel so there's no real notion of direction. */
50513 }
50514
50515 /*
50516 In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable.
50517 The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to
50518 0, panning will be most extreme and any sounds that are positioned on the opposite side of the
50519 speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it
50520 doesn't even remotely represent the real world at all because sounds that come from your right side
50521 are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at
50522 all, which is also not ideal. By setting it to something greater than 0, the spatialization effect
50523 becomes much less dramatic and a lot more bearable.
50524
50525 Summary: 0 = more extreme panning; 1 = no panning.
50526 */
50527 dMin = pSpatializer->minSpatializationChannelGain;
50528
50529 /*
50530 At this point, "d" will be positive if the sound is on the same side as the channel and negative if
50531 it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to
50532 calculate a panning value. The first is to simply convert it to 0..1, however this has a problem
50533 which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right
50534 in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like
50535 the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front
50536 of the listener. I would intuitively expect that to be played at full volume, or close to it.
50537
50538 The second idea I think of is to only apply a reduction in gain when the sound is on the opposite
50539 side of the speaker. That is, reduce the gain only when the dot product is negative. The problem
50540 with this is that there will not be any attenuation as the sound sweeps around the 180 degrees
50541 where the dot product is positive. The idea with this option is that you leave the gain at 1 when
50542 the sound is being played on the same side as the speaker and then you just reduce the volume when
50543 the sound is on the other side.
50544
50545 The summarize, I think the first option should give a better sense of spatialization, but the second
50546 option is better for preserving the sound's power.
50547
50548 UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a
50549 bit better, but you can also hear the reduction in volume when it's right in front.
50550 */
50551 #if 1
50552 {
50553 /*
50554 Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power
50555 by being played at 0.5 gain.
50556 */
50557 d = (d + 1) * 0.5f; /* -1..1 to 0..1 */
50558 d = ma_max(d, dMin);
50559 pSpatializer->pNewChannelGainsOut[iChannel] *= d;
50560 }
50561 #else
50562 {
50563 /*
50564 Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more
50565 consistent, but comes at the expense of a worse sense of space and positioning.
50566 */
50567 if (d < 0) {
50568 d += 1; /* Move into the positive range. */
50569 d = ma_max(d, dMin);
50570 channelGainsOut[iChannel] *= d;
50571 }
50572 }
50573 #endif
50574 }
50575 } else {
50576 /* Assume the sound is right on top of us. Don't do any panning. */
50577 }
50578
50579 /* 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. */
50580 ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut);
50581 ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount);
50582
50583 /*
50584 Before leaving we'll want to update our doppler pitch so that the caller can apply some
50585 pitch shifting if they desire. Note that we need to negate the relative position here
50586 because the doppler calculation needs to be source-to-listener, but ours is listener-to-
50587 source.
50588 */
50589 if (dopplerFactor > 0) {
50590 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);
50591 } else {
50592 pSpatializer->dopplerPitch = 1;
50593 }
50594 }
50595
50596 return MA_SUCCESS;
50597 }
50598
50599 MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume)
50600 {
50601 if (pSpatializer == NULL) {
50602 return MA_INVALID_ARGS;
50603 }
50604
50605 return ma_gainer_set_master_volume(&pSpatializer->gainer, volume);
50606 }
50607
50608 MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume)
50609 {
50610 if (pSpatializer == NULL) {
50611 return MA_INVALID_ARGS;
50612 }
50613
50614 return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume);
50615 }
50616
50617 MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer)
50618 {
50619 if (pSpatializer == NULL) {
50620 return 0;
50621 }
50622
50623 return pSpatializer->channelsIn;
50624 }
50625
50626 MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer)
50627 {
50628 if (pSpatializer == NULL) {
50629 return 0;
50630 }
50631
50632 return pSpatializer->channelsOut;
50633 }
50634
50635 MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel)
50636 {
50637 if (pSpatializer == NULL) {
50638 return;
50639 }
50640
50641 c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel);
50642 }
50643
50644 MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer)
50645 {
50646 if (pSpatializer == NULL) {
50647 return ma_attenuation_model_none;
50648 }
50649
50650 return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel);
50651 }
50652
50653 MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning)
50654 {
50655 if (pSpatializer == NULL) {
50656 return;
50657 }
50658
50659 c89atomic_exchange_i32(&pSpatializer->positioning, positioning);
50660 }
50661
50662 MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer)
50663 {
50664 if (pSpatializer == NULL) {
50665 return ma_positioning_absolute;
50666 }
50667
50668 return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning);
50669 }
50670
50671 MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff)
50672 {
50673 if (pSpatializer == NULL) {
50674 return;
50675 }
50676
50677 c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff);
50678 }
50679
50680 MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer)
50681 {
50682 if (pSpatializer == NULL) {
50683 return 0;
50684 }
50685
50686 return c89atomic_load_f32(&pSpatializer->rolloff);
50687 }
50688
50689 MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain)
50690 {
50691 if (pSpatializer == NULL) {
50692 return;
50693 }
50694
50695 c89atomic_exchange_f32(&pSpatializer->minGain, minGain);
50696 }
50697
50698 MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer)
50699 {
50700 if (pSpatializer == NULL) {
50701 return 0;
50702 }
50703
50704 return c89atomic_load_f32(&pSpatializer->minGain);
50705 }
50706
50707 MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain)
50708 {
50709 if (pSpatializer == NULL) {
50710 return;
50711 }
50712
50713 c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain);
50714 }
50715
50716 MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer)
50717 {
50718 if (pSpatializer == NULL) {
50719 return 0;
50720 }
50721
50722 return c89atomic_load_f32(&pSpatializer->maxGain);
50723 }
50724
50725 MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance)
50726 {
50727 if (pSpatializer == NULL) {
50728 return;
50729 }
50730
50731 c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance);
50732 }
50733
50734 MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer)
50735 {
50736 if (pSpatializer == NULL) {
50737 return 0;
50738 }
50739
50740 return c89atomic_load_f32(&pSpatializer->minDistance);
50741 }
50742
50743 MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance)
50744 {
50745 if (pSpatializer == NULL) {
50746 return;
50747 }
50748
50749 c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance);
50750 }
50751
50752 MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer)
50753 {
50754 if (pSpatializer == NULL) {
50755 return 0;
50756 }
50757
50758 return c89atomic_load_f32(&pSpatializer->maxDistance);
50759 }
50760
50761 MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
50762 {
50763 if (pSpatializer == NULL) {
50764 return;
50765 }
50766
50767 c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians);
50768 c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians);
50769 c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain);
50770 }
50771
50772 MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
50773 {
50774 if (pSpatializer == NULL) {
50775 return;
50776 }
50777
50778 if (pInnerAngleInRadians != NULL) {
50779 *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians);
50780 }
50781
50782 if (pOuterAngleInRadians != NULL) {
50783 *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians);
50784 }
50785
50786 if (pOuterGain != NULL) {
50787 *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain);
50788 }
50789 }
50790
50791 MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor)
50792 {
50793 if (pSpatializer == NULL) {
50794 return;
50795 }
50796
50797 c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor);
50798 }
50799
50800 MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer)
50801 {
50802 if (pSpatializer == NULL) {
50803 return 1;
50804 }
50805
50806 return c89atomic_load_f32(&pSpatializer->dopplerFactor);
50807 }
50808
50809 MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor)
50810 {
50811 if (pSpatializer == NULL) {
50812 return;
50813 }
50814
50815 c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor);
50816 }
50817
50818 MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer)
50819 {
50820 if (pSpatializer == NULL) {
50821 return 1;
50822 }
50823
50824 return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor);
50825 }
50826
50827 MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z)
50828 {
50829 if (pSpatializer == NULL) {
50830 return;
50831 }
50832
50833 ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z));
50834 }
50835
50836 MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer)
50837 {
50838 if (pSpatializer == NULL) {
50839 return ma_vec3f_init_3f(0, 0, 0);
50840 }
50841
50842 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. */
50843 }
50844
50845 MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z)
50846 {
50847 if (pSpatializer == NULL) {
50848 return;
50849 }
50850
50851 ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z));
50852 }
50853
50854 MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer)
50855 {
50856 if (pSpatializer == NULL) {
50857 return ma_vec3f_init_3f(0, 0, -1);
50858 }
50859
50860 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. */
50861 }
50862
50863 MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z)
50864 {
50865 if (pSpatializer == NULL) {
50866 return;
50867 }
50868
50869 ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z));
50870 }
50871
50872 MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer)
50873 {
50874 if (pSpatializer == NULL) {
50875 return ma_vec3f_init_3f(0, 0, 0);
50876 }
50877
50878 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. */
50879 }
50880
50881 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)
50882 {
50883 if (pRelativePos != NULL) {
50884 pRelativePos->x = 0;
50885 pRelativePos->y = 0;
50886 pRelativePos->z = 0;
50887 }
50888
50889 if (pRelativeDir != NULL) {
50890 pRelativeDir->x = 0;
50891 pRelativeDir->y = 0;
50892 pRelativeDir->z = -1;
50893 }
50894
50895 if (pSpatializer == NULL) {
50896 return;
50897 }
50898
50899 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
50900 /* There's no listener or we're using relative positioning. */
50901 if (pRelativePos != NULL) {
50902 *pRelativePos = ma_spatializer_get_position(pSpatializer);
50903 }
50904 if (pRelativeDir != NULL) {
50905 *pRelativeDir = ma_spatializer_get_direction(pSpatializer);
50906 }
50907 } else {
50908 ma_vec3f spatializerPosition;
50909 ma_vec3f spatializerDirection;
50910 ma_vec3f listenerPosition;
50911 ma_vec3f listenerDirection;
50912 ma_vec3f v;
50913 ma_vec3f axisX;
50914 ma_vec3f axisY;
50915 ma_vec3f axisZ;
50916 float m[4][4];
50917
50918 spatializerPosition = ma_spatializer_get_position(pSpatializer);
50919 spatializerDirection = ma_spatializer_get_direction(pSpatializer);
50920 listenerPosition = ma_spatializer_listener_get_position(pListener);
50921 listenerDirection = ma_spatializer_listener_get_direction(pListener);
50922
50923 /*
50924 We need to calcualte the right vector from our forward and up vectors. This is done with
50925 a cross product.
50926 */
50927 axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */
50928 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. */
50929
50930 /*
50931 The calculation of axisX above can result in a zero-length vector if the listener is
50932 looking straight up on the Y axis. We'll need to fall back to a +X in this case so that
50933 the calculations below don't fall apart. This is where a quaternion based listener and
50934 sound orientation would come in handy.
50935 */
50936 if (ma_vec3f_len2(axisX) == 0) {
50937 axisX = ma_vec3f_init_3f(1, 0, 0);
50938 }
50939
50940 axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */
50941
50942 /*
50943 We need to swap the X axis if we're left handed because otherwise the cross product above
50944 will have resulted in it pointing in the wrong direction (right handed was assumed in the
50945 cross products above).
50946 */
50947 if (pListener->config.handedness == ma_handedness_left) {
50948 axisX = ma_vec3f_neg(axisX);
50949 }
50950
50951 /* Lookat. */
50952 m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition);
50953 m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition);
50954 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);
50955 m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1;
50956
50957 /*
50958 Multiply the lookat matrix by the spatializer position to transform it to listener
50959 space. This allows calculations to work based on the sound being relative to the
50960 origin which makes things simpler.
50961 */
50962 if (pRelativePos != NULL) {
50963 v = spatializerPosition;
50964 pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1;
50965 pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1;
50966 pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1;
50967 }
50968
50969 /*
50970 The direction of the sound needs to also be transformed so that it's relative to the
50971 rotation of the listener.
50972 */
50973 if (pRelativeDir != NULL) {
50974 v = spatializerDirection;
50975 pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z;
50976 pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z;
50977 pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z;
50978 }
50979 }
50980 }
50981
50982
50983
50984
50985 /**************************************************************************************************************************************************************
50986
50987 Resampling
50988
50989 **************************************************************************************************************************************************************/
50990 MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
50991 {
50992 ma_linear_resampler_config config;
50993 MA_ZERO_OBJECT(&config);
50994 config.format = format;
50995 config.channels = channels;
50996 config.sampleRateIn = sampleRateIn;
50997 config.sampleRateOut = sampleRateOut;
50998 config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
50999 config.lpfNyquistFactor = 1;
51000
51001 return config;
51002 }
51003
51004
51005 typedef struct
51006 {
51007 size_t sizeInBytes;
51008 size_t x0Offset;
51009 size_t x1Offset;
51010 size_t lpfOffset;
51011 } ma_linear_resampler_heap_layout;
51012
51013
51014 static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
51015 {
51016 /*
51017 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
51018 be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
51019 */
51020 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. */
51021 ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
51022
51023 pResampler->inTimeFrac =
51024 (oldRateTimeWhole * newSampleRateOut) +
51025 ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);
51026
51027 /* Make sure the fractional part is less than the output sample rate. */
51028 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
51029 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
51030 }
51031
51032 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)
51033 {
51034 ma_result result;
51035 ma_uint32 gcf;
51036 ma_uint32 lpfSampleRate;
51037 double lpfCutoffFrequency;
51038 ma_lpf_config lpfConfig;
51039 ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
51040
51041 if (pResampler == NULL) {
51042 return MA_INVALID_ARGS;
51043 }
51044
51045 if (sampleRateIn == 0 || sampleRateOut == 0) {
51046 return MA_INVALID_ARGS;
51047 }
51048
51049 oldSampleRateOut = pResampler->config.sampleRateOut;
51050
51051 pResampler->config.sampleRateIn = sampleRateIn;
51052 pResampler->config.sampleRateOut = sampleRateOut;
51053
51054 /* Simplify the sample rate. */
51055 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
51056 pResampler->config.sampleRateIn /= gcf;
51057 pResampler->config.sampleRateOut /= gcf;
51058
51059 /* Always initialize the low-pass filter, even when the order is 0. */
51060 if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
51061 return MA_INVALID_ARGS;
51062 }
51063
51064 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
51065 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
51066
51067 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
51068
51069 /*
51070 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
51071 getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
51072 */
51073 if (isResamplerAlreadyInitialized) {
51074 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
51075 } else {
51076 result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf);
51077 }
51078
51079 if (result != MA_SUCCESS) {
51080 return result;
51081 }
51082
51083
51084 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
51085 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
51086
51087 /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
51088 ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
51089
51090 return MA_SUCCESS;
51091 }
51092
51093 static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
51094 {
51095 MA_ASSERT(pHeapLayout != NULL);
51096
51097 MA_ZERO_OBJECT(pHeapLayout);
51098
51099 if (pConfig == NULL) {
51100 return MA_INVALID_ARGS;
51101 }
51102
51103 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
51104 return MA_INVALID_ARGS;
51105 }
51106
51107 if (pConfig->channels == 0) {
51108 return MA_INVALID_ARGS;
51109 }
51110
51111 pHeapLayout->sizeInBytes = 0;
51112
51113 /* x0 */
51114 pHeapLayout->x0Offset = pHeapLayout->sizeInBytes;
51115 if (pConfig->format == ma_format_f32) {
51116 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
51117 } else {
51118 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
51119 }
51120
51121 /* x1 */
51122 pHeapLayout->x1Offset = pHeapLayout->sizeInBytes;
51123 if (pConfig->format == ma_format_f32) {
51124 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
51125 } else {
51126 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
51127 }
51128
51129 /* LPF */
51130 pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes);
51131 {
51132 ma_result result;
51133 size_t lpfHeapSizeInBytes;
51134 ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */
51135
51136 result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);
51137 if (result != MA_SUCCESS) {
51138 return result;
51139 }
51140
51141 pHeapLayout->sizeInBytes += lpfHeapSizeInBytes;
51142 }
51143
51144 /* Make sure allocation size is aligned. */
51145 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
51146
51147 return MA_SUCCESS;
51148 }
51149
51150 MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes)
51151 {
51152 ma_result result;
51153 ma_linear_resampler_heap_layout heapLayout;
51154
51155 if (pHeapSizeInBytes == NULL) {
51156 return MA_INVALID_ARGS;
51157 }
51158
51159 *pHeapSizeInBytes = 0;
51160
51161 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
51162 if (result != MA_SUCCESS) {
51163 return result;
51164 }
51165
51166 *pHeapSizeInBytes = heapLayout.sizeInBytes;
51167
51168 return MA_SUCCESS;
51169 }
51170
51171 MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler)
51172 {
51173 ma_result result;
51174 ma_linear_resampler_heap_layout heapLayout;
51175
51176 if (pResampler == NULL) {
51177 return MA_INVALID_ARGS;
51178 }
51179
51180 MA_ZERO_OBJECT(pResampler);
51181
51182 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
51183 if (result != MA_SUCCESS) {
51184 return result;
51185 }
51186
51187 pResampler->config = *pConfig;
51188
51189 pResampler->_pHeap = pHeap;
51190 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
51191
51192 if (pConfig->format == ma_format_f32) {
51193 pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
51194 pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
51195 } else {
51196 pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
51197 pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
51198 }
51199
51200 /* Setting the rate will set up the filter and time advances for us. */
51201 result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
51202 if (result != MA_SUCCESS) {
51203 return result;
51204 }
51205
51206 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
51207 pResampler->inTimeFrac = 0;
51208
51209 return MA_SUCCESS;
51210 }
51211
51212 MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler)
51213 {
51214 ma_result result;
51215 size_t heapSizeInBytes;
51216 void* pHeap;
51217
51218 result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes);
51219 if (result != MA_SUCCESS) {
51220 return result;
51221 }
51222
51223 if (heapSizeInBytes > 0) {
51224 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
51225 if (pHeap == NULL) {
51226 return MA_OUT_OF_MEMORY;
51227 }
51228 } else {
51229 pHeap = NULL;
51230 }
51231
51232 result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler);
51233 if (result != MA_SUCCESS) {
51234 ma_free(pHeap, pAllocationCallbacks);
51235 return result;
51236 }
51237
51238 pResampler->_ownsHeap = MA_TRUE;
51239 return MA_SUCCESS;
51240 }
51241
51242 MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
51243 {
51244 if (pResampler == NULL) {
51245 return;
51246 }
51247
51248 ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);
51249
51250 if (pResampler->_ownsHeap) {
51251 ma_free(pResampler->_pHeap, pAllocationCallbacks);
51252 }
51253 }
51254
51255 static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
51256 {
51257 ma_int32 b;
51258 ma_int32 c;
51259 ma_int32 r;
51260
51261 MA_ASSERT(a <= (1<<shift));
51262
51263 b = x * ((1<<shift) - a);
51264 c = y * a;
51265 r = b + c;
51266
51267 return (ma_int16)(r >> shift);
51268 }
51269
51270 static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
51271 {
51272 ma_uint32 c;
51273 ma_uint32 a;
51274 const ma_uint32 channels = pResampler->config.channels;
51275 const ma_uint32 shift = 12;
51276
51277 MA_ASSERT(pResampler != NULL);
51278 MA_ASSERT(pFrameOut != NULL);
51279
51280 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
51281
51282 MA_ASSUME(channels > 0);
51283 for (c = 0; c < channels; c += 1) {
51284 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
51285 pFrameOut[c] = s;
51286 }
51287 }
51288
51289
51290 static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)
51291 {
51292 ma_uint32 c;
51293 float a;
51294 const ma_uint32 channels = pResampler->config.channels;
51295
51296 MA_ASSERT(pResampler != NULL);
51297 MA_ASSERT(pFrameOut != NULL);
51298
51299 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
51300
51301 MA_ASSUME(channels > 0);
51302 for (c = 0; c < channels; c += 1) {
51303 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
51304 pFrameOut[c] = s;
51305 }
51306 }
51307
51308 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)
51309 {
51310 const ma_int16* pFramesInS16;
51311 /* */ ma_int16* pFramesOutS16;
51312 ma_uint64 frameCountIn;
51313 ma_uint64 frameCountOut;
51314 ma_uint64 framesProcessedIn;
51315 ma_uint64 framesProcessedOut;
51316
51317 MA_ASSERT(pResampler != NULL);
51318 MA_ASSERT(pFrameCountIn != NULL);
51319 MA_ASSERT(pFrameCountOut != NULL);
51320
51321 pFramesInS16 = (const ma_int16*)pFramesIn;
51322 pFramesOutS16 = ( ma_int16*)pFramesOut;
51323 frameCountIn = *pFrameCountIn;
51324 frameCountOut = *pFrameCountOut;
51325 framesProcessedIn = 0;
51326 framesProcessedOut = 0;
51327
51328 while (framesProcessedOut < frameCountOut) {
51329 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
51330 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51331 ma_uint32 iChannel;
51332
51333 if (pFramesInS16 != NULL) {
51334 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51335 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51336 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
51337 }
51338 pFramesInS16 += pResampler->config.channels;
51339 } else {
51340 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51341 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51342 pResampler->x1.s16[iChannel] = 0;
51343 }
51344 }
51345
51346 /* Filter. */
51347 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
51348
51349 framesProcessedIn += 1;
51350 pResampler->inTimeInt -= 1;
51351 }
51352
51353 if (pResampler->inTimeInt > 0) {
51354 break; /* Ran out of input data. */
51355 }
51356
51357 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
51358 if (pFramesOutS16 != NULL) {
51359 MA_ASSERT(pResampler->inTimeInt == 0);
51360 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
51361
51362 pFramesOutS16 += pResampler->config.channels;
51363 }
51364
51365 framesProcessedOut += 1;
51366
51367 /* Advance time forward. */
51368 pResampler->inTimeInt += pResampler->inAdvanceInt;
51369 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51370 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51371 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51372 pResampler->inTimeInt += 1;
51373 }
51374 }
51375
51376 *pFrameCountIn = framesProcessedIn;
51377 *pFrameCountOut = framesProcessedOut;
51378
51379 return MA_SUCCESS;
51380 }
51381
51382 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)
51383 {
51384 const ma_int16* pFramesInS16;
51385 /* */ ma_int16* pFramesOutS16;
51386 ma_uint64 frameCountIn;
51387 ma_uint64 frameCountOut;
51388 ma_uint64 framesProcessedIn;
51389 ma_uint64 framesProcessedOut;
51390
51391 MA_ASSERT(pResampler != NULL);
51392 MA_ASSERT(pFrameCountIn != NULL);
51393 MA_ASSERT(pFrameCountOut != NULL);
51394
51395 pFramesInS16 = (const ma_int16*)pFramesIn;
51396 pFramesOutS16 = ( ma_int16*)pFramesOut;
51397 frameCountIn = *pFrameCountIn;
51398 frameCountOut = *pFrameCountOut;
51399 framesProcessedIn = 0;
51400 framesProcessedOut = 0;
51401
51402 while (framesProcessedOut < frameCountOut) {
51403 /* Before interpolating we need to load the buffers. */
51404 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51405 ma_uint32 iChannel;
51406
51407 if (pFramesInS16 != NULL) {
51408 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51409 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51410 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
51411 }
51412 pFramesInS16 += pResampler->config.channels;
51413 } else {
51414 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51415 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
51416 pResampler->x1.s16[iChannel] = 0;
51417 }
51418 }
51419
51420 framesProcessedIn += 1;
51421 pResampler->inTimeInt -= 1;
51422 }
51423
51424 if (pResampler->inTimeInt > 0) {
51425 break; /* Ran out of input data. */
51426 }
51427
51428 /* Getting here means the frames have been loaded and we can generate the next output frame. */
51429 if (pFramesOutS16 != NULL) {
51430 MA_ASSERT(pResampler->inTimeInt == 0);
51431 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
51432
51433 /* Filter. */
51434 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
51435
51436 pFramesOutS16 += pResampler->config.channels;
51437 }
51438
51439 framesProcessedOut += 1;
51440
51441 /* Advance time forward. */
51442 pResampler->inTimeInt += pResampler->inAdvanceInt;
51443 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51444 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51445 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51446 pResampler->inTimeInt += 1;
51447 }
51448 }
51449
51450 *pFrameCountIn = framesProcessedIn;
51451 *pFrameCountOut = framesProcessedOut;
51452
51453 return MA_SUCCESS;
51454 }
51455
51456 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)
51457 {
51458 MA_ASSERT(pResampler != NULL);
51459
51460 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
51461 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51462 } else {
51463 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51464 }
51465 }
51466
51467
51468 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)
51469 {
51470 const float* pFramesInF32;
51471 /* */ float* pFramesOutF32;
51472 ma_uint64 frameCountIn;
51473 ma_uint64 frameCountOut;
51474 ma_uint64 framesProcessedIn;
51475 ma_uint64 framesProcessedOut;
51476
51477 MA_ASSERT(pResampler != NULL);
51478 MA_ASSERT(pFrameCountIn != NULL);
51479 MA_ASSERT(pFrameCountOut != NULL);
51480
51481 pFramesInF32 = (const float*)pFramesIn;
51482 pFramesOutF32 = ( float*)pFramesOut;
51483 frameCountIn = *pFrameCountIn;
51484 frameCountOut = *pFrameCountOut;
51485 framesProcessedIn = 0;
51486 framesProcessedOut = 0;
51487
51488 while (framesProcessedOut < frameCountOut) {
51489 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
51490 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51491 ma_uint32 iChannel;
51492
51493 if (pFramesInF32 != NULL) {
51494 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51495 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51496 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
51497 }
51498 pFramesInF32 += pResampler->config.channels;
51499 } else {
51500 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51501 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51502 pResampler->x1.f32[iChannel] = 0;
51503 }
51504 }
51505
51506 /* Filter. */
51507 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
51508
51509 framesProcessedIn += 1;
51510 pResampler->inTimeInt -= 1;
51511 }
51512
51513 if (pResampler->inTimeInt > 0) {
51514 break; /* Ran out of input data. */
51515 }
51516
51517 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
51518 if (pFramesOutF32 != NULL) {
51519 MA_ASSERT(pResampler->inTimeInt == 0);
51520 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
51521
51522 pFramesOutF32 += pResampler->config.channels;
51523 }
51524
51525 framesProcessedOut += 1;
51526
51527 /* Advance time forward. */
51528 pResampler->inTimeInt += pResampler->inAdvanceInt;
51529 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51530 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51531 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51532 pResampler->inTimeInt += 1;
51533 }
51534 }
51535
51536 *pFrameCountIn = framesProcessedIn;
51537 *pFrameCountOut = framesProcessedOut;
51538
51539 return MA_SUCCESS;
51540 }
51541
51542 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)
51543 {
51544 const float* pFramesInF32;
51545 /* */ float* pFramesOutF32;
51546 ma_uint64 frameCountIn;
51547 ma_uint64 frameCountOut;
51548 ma_uint64 framesProcessedIn;
51549 ma_uint64 framesProcessedOut;
51550
51551 MA_ASSERT(pResampler != NULL);
51552 MA_ASSERT(pFrameCountIn != NULL);
51553 MA_ASSERT(pFrameCountOut != NULL);
51554
51555 pFramesInF32 = (const float*)pFramesIn;
51556 pFramesOutF32 = ( float*)pFramesOut;
51557 frameCountIn = *pFrameCountIn;
51558 frameCountOut = *pFrameCountOut;
51559 framesProcessedIn = 0;
51560 framesProcessedOut = 0;
51561
51562 while (framesProcessedOut < frameCountOut) {
51563 /* Before interpolating we need to load the buffers. */
51564 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
51565 ma_uint32 iChannel;
51566
51567 if (pFramesInF32 != NULL) {
51568 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51569 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51570 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
51571 }
51572 pFramesInF32 += pResampler->config.channels;
51573 } else {
51574 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51575 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
51576 pResampler->x1.f32[iChannel] = 0;
51577 }
51578 }
51579
51580 framesProcessedIn += 1;
51581 pResampler->inTimeInt -= 1;
51582 }
51583
51584 if (pResampler->inTimeInt > 0) {
51585 break; /* Ran out of input data. */
51586 }
51587
51588 /* Getting here means the frames have been loaded and we can generate the next output frame. */
51589 if (pFramesOutF32 != NULL) {
51590 MA_ASSERT(pResampler->inTimeInt == 0);
51591 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
51592
51593 /* Filter. */
51594 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
51595
51596 pFramesOutF32 += pResampler->config.channels;
51597 }
51598
51599 framesProcessedOut += 1;
51600
51601 /* Advance time forward. */
51602 pResampler->inTimeInt += pResampler->inAdvanceInt;
51603 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
51604 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
51605 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
51606 pResampler->inTimeInt += 1;
51607 }
51608 }
51609
51610 *pFrameCountIn = framesProcessedIn;
51611 *pFrameCountOut = framesProcessedOut;
51612
51613 return MA_SUCCESS;
51614 }
51615
51616 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)
51617 {
51618 MA_ASSERT(pResampler != NULL);
51619
51620 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
51621 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51622 } else {
51623 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51624 }
51625 }
51626
51627
51628 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)
51629 {
51630 if (pResampler == NULL) {
51631 return MA_INVALID_ARGS;
51632 }
51633
51634 /* */ if (pResampler->config.format == ma_format_s16) {
51635 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51636 } else if (pResampler->config.format == ma_format_f32) {
51637 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51638 } else {
51639 /* 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(). */
51640 MA_ASSERT(MA_FALSE);
51641 return MA_INVALID_ARGS;
51642 }
51643 }
51644
51645
51646 MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
51647 {
51648 return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
51649 }
51650
51651 MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
51652 {
51653 ma_uint32 n;
51654 ma_uint32 d;
51655
51656 if (pResampler == NULL) {
51657 return MA_INVALID_ARGS;
51658 }
51659
51660 if (ratioInOut <= 0) {
51661 return MA_INVALID_ARGS;
51662 }
51663
51664 d = 1000;
51665 n = (ma_uint32)(ratioInOut * d);
51666
51667 if (n == 0) {
51668 return MA_INVALID_ARGS; /* Ratio too small. */
51669 }
51670
51671 MA_ASSERT(n != 0);
51672
51673 return ma_linear_resampler_set_rate(pResampler, n, d);
51674 }
51675
51676 MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler)
51677 {
51678 if (pResampler == NULL) {
51679 return 0;
51680 }
51681
51682 return 1 + ma_lpf_get_latency(&pResampler->lpf);
51683 }
51684
51685 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)
51686 {
51687 if (pResampler == NULL) {
51688 return 0;
51689 }
51690
51691 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
51692 }
51693
51694 MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
51695 {
51696 ma_uint64 inputFrameCount;
51697
51698 if (pInputFrameCount == NULL) {
51699 return MA_INVALID_ARGS;
51700 }
51701
51702 *pInputFrameCount = 0;
51703
51704 if (pResampler == NULL) {
51705 return MA_INVALID_ARGS;
51706 }
51707
51708 if (outputFrameCount == 0) {
51709 return MA_SUCCESS;
51710 }
51711
51712 /* Any whole input frames are consumed before the first output frame is generated. */
51713 inputFrameCount = pResampler->inTimeInt;
51714 outputFrameCount -= 1;
51715
51716 /* The rest of the output frames can be calculated in constant time. */
51717 inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
51718 inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
51719
51720 *pInputFrameCount = inputFrameCount;
51721
51722 return MA_SUCCESS;
51723 }
51724
51725 MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
51726 {
51727 ma_uint64 outputFrameCount;
51728 ma_uint64 preliminaryInputFrameCountFromFrac;
51729 ma_uint64 preliminaryInputFrameCount;
51730
51731 if (pOutputFrameCount == NULL) {
51732 return MA_INVALID_ARGS;
51733 }
51734
51735 *pOutputFrameCount = 0;
51736
51737 if (pResampler == NULL) {
51738 return MA_INVALID_ARGS;
51739 }
51740
51741 /*
51742 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
51743 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
51744 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
51745 of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
51746 */
51747 outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;
51748
51749 /*
51750 We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
51751 used in the logic below to determine whether or not we need to add an extra output frame.
51752 */
51753 preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;
51754 preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
51755
51756 /*
51757 If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than
51758 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
51759 to actually process. Otherwise we need to add the extra output frame.
51760 */
51761 if (preliminaryInputFrameCount <= inputFrameCount) {
51762 outputFrameCount += 1;
51763 }
51764
51765 *pOutputFrameCount = outputFrameCount;
51766
51767 return MA_SUCCESS;
51768 }
51769
51770 MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
51771 {
51772 ma_uint32 iChannel;
51773
51774 if (pResampler == NULL) {
51775 return MA_INVALID_ARGS;
51776 }
51777
51778 /* Timers need to be cleared back to zero. */
51779 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
51780 pResampler->inTimeFrac = 0;
51781
51782 /* Cached samples need to be cleared. */
51783 if (pResampler->config.format == ma_format_f32) {
51784 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51785 pResampler->x0.f32[iChannel] = 0;
51786 pResampler->x1.f32[iChannel] = 0;
51787 }
51788 } else {
51789 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
51790 pResampler->x0.s16[iChannel] = 0;
51791 pResampler->x1.s16[iChannel] = 0;
51792 }
51793 }
51794
51795 /* The low pass filter needs to have it's cache reset. */
51796 ma_lpf_clear_cache(&pResampler->lpf);
51797
51798 return MA_SUCCESS;
51799 }
51800
51801
51802
51803 /* Linear resampler backend vtable. */
51804 static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig)
51805 {
51806 ma_linear_resampler_config linearConfig;
51807
51808 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
51809 linearConfig.lpfOrder = pConfig->linear.lpfOrder;
51810
51811 return linearConfig;
51812 }
51813
51814 static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
51815 {
51816 ma_linear_resampler_config linearConfig;
51817
51818 (void)pUserData;
51819
51820 linearConfig = ma_resampling_backend_get_config__linear(pConfig);
51821
51822 return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes);
51823 }
51824
51825 static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend)
51826 {
51827 ma_resampler* pResampler = (ma_resampler*)pUserData;
51828 ma_result result;
51829 ma_linear_resampler_config linearConfig;
51830
51831 (void)pUserData;
51832
51833 linearConfig = ma_resampling_backend_get_config__linear(pConfig);
51834
51835 result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear);
51836 if (result != MA_SUCCESS) {
51837 return result;
51838 }
51839
51840 *ppBackend = &pResampler->state.linear;
51841
51842 return MA_SUCCESS;
51843 }
51844
51845 static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
51846 {
51847 (void)pUserData;
51848
51849 ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks);
51850 }
51851
51852 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)
51853 {
51854 (void)pUserData;
51855
51856 return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
51857 }
51858
51859 static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
51860 {
51861 (void)pUserData;
51862
51863 return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut);
51864 }
51865
51866 static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
51867 {
51868 (void)pUserData;
51869
51870 return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend);
51871 }
51872
51873 static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
51874 {
51875 (void)pUserData;
51876
51877 return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend);
51878 }
51879
51880 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)
51881 {
51882 (void)pUserData;
51883
51884 return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount);
51885 }
51886
51887 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)
51888 {
51889 (void)pUserData;
51890
51891 return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);
51892 }
51893
51894 static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend)
51895 {
51896 (void)pUserData;
51897
51898 return ma_linear_resampler_reset((ma_linear_resampler*)pBackend);
51899 }
51900
51901 static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
51902 {
51903 ma_resampling_backend_get_heap_size__linear,
51904 ma_resampling_backend_init__linear,
51905 ma_resampling_backend_uninit__linear,
51906 ma_resampling_backend_process__linear,
51907 ma_resampling_backend_set_rate__linear,
51908 ma_resampling_backend_get_input_latency__linear,
51909 ma_resampling_backend_get_output_latency__linear,
51910 ma_resampling_backend_get_required_input_frame_count__linear,
51911 ma_resampling_backend_get_expected_output_frame_count__linear,
51912 ma_resampling_backend_reset__linear
51913 };
51914
51915
51916
51917 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)
51918 {
51919 ma_resampler_config config;
51920
51921 MA_ZERO_OBJECT(&config);
51922 config.format = format;
51923 config.channels = channels;
51924 config.sampleRateIn = sampleRateIn;
51925 config.sampleRateOut = sampleRateOut;
51926 config.algorithm = algorithm;
51927
51928 /* Linear. */
51929 config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
51930
51931 return config;
51932 }
51933
51934 static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData)
51935 {
51936 MA_ASSERT(pConfig != NULL);
51937 MA_ASSERT(ppVTable != NULL);
51938 MA_ASSERT(ppUserData != NULL);
51939
51940 /* Safety. */
51941 *ppVTable = NULL;
51942 *ppUserData = NULL;
51943
51944 switch (pConfig->algorithm)
51945 {
51946 case ma_resample_algorithm_linear:
51947 {
51948 *ppVTable = &g_ma_linear_resampler_vtable;
51949 *ppUserData = pResampler;
51950 } break;
51951
51952 case ma_resample_algorithm_custom:
51953 {
51954 *ppVTable = pConfig->pBackendVTable;
51955 *ppUserData = pConfig->pBackendUserData;
51956 } break;
51957
51958 default: return MA_INVALID_ARGS;
51959 }
51960
51961 return MA_SUCCESS;
51962 }
51963
51964 MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
51965 {
51966 ma_result result;
51967 ma_resampling_backend_vtable* pVTable;
51968 void* pVTableUserData;
51969
51970 if (pHeapSizeInBytes == NULL) {
51971 return MA_INVALID_ARGS;
51972 }
51973
51974 *pHeapSizeInBytes = 0;
51975
51976 if (pConfig == NULL) {
51977 return MA_INVALID_ARGS;
51978 }
51979
51980 result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData);
51981 if (result != MA_SUCCESS) {
51982 return result;
51983 }
51984
51985 if (pVTable == NULL || pVTable->onGetHeapSize == NULL) {
51986 return MA_NOT_IMPLEMENTED;
51987 }
51988
51989 result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes);
51990 if (result != MA_SUCCESS) {
51991 return result;
51992 }
51993
51994 return MA_SUCCESS;
51995 }
51996
51997 MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler)
51998 {
51999 ma_result result;
52000
52001 if (pResampler == NULL) {
52002 return MA_INVALID_ARGS;
52003 }
52004
52005 MA_ZERO_OBJECT(pResampler);
52006
52007 if (pConfig == NULL) {
52008 return MA_INVALID_ARGS;
52009 }
52010
52011 pResampler->_pHeap = pHeap;
52012 pResampler->format = pConfig->format;
52013 pResampler->channels = pConfig->channels;
52014 pResampler->sampleRateIn = pConfig->sampleRateIn;
52015 pResampler->sampleRateOut = pConfig->sampleRateOut;
52016
52017 result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData);
52018 if (result != MA_SUCCESS) {
52019 return result;
52020 }
52021
52022 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) {
52023 return MA_NOT_IMPLEMENTED; /* onInit not implemented. */
52024 }
52025
52026 result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend);
52027 if (result != MA_SUCCESS) {
52028 return result;
52029 }
52030
52031 return MA_SUCCESS;
52032 }
52033
52034 MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler)
52035 {
52036 ma_result result;
52037 size_t heapSizeInBytes;
52038 void* pHeap;
52039
52040 result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes);
52041 if (result != MA_SUCCESS) {
52042 return result;
52043 }
52044
52045 if (heapSizeInBytes > 0) {
52046 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
52047 if (pHeap == NULL) {
52048 return MA_OUT_OF_MEMORY;
52049 }
52050 } else {
52051 pHeap = NULL;
52052 }
52053
52054 result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler);
52055 if (result != MA_SUCCESS) {
52056 ma_free(pHeap, pAllocationCallbacks);
52057 return result;
52058 }
52059
52060 pResampler->_ownsHeap = MA_TRUE;
52061 return MA_SUCCESS;
52062 }
52063
52064 MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
52065 {
52066 if (pResampler == NULL) {
52067 return;
52068 }
52069
52070 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) {
52071 return;
52072 }
52073
52074 pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks);
52075
52076 if (pResampler->_ownsHeap) {
52077 ma_free(pResampler->_pHeap, pAllocationCallbacks);
52078 }
52079 }
52080
52081 MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
52082 {
52083 if (pResampler == NULL) {
52084 return MA_INVALID_ARGS;
52085 }
52086
52087 if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
52088 return MA_INVALID_ARGS;
52089 }
52090
52091 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) {
52092 return MA_NOT_IMPLEMENTED;
52093 }
52094
52095 return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
52096 }
52097
52098 MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
52099 {
52100 ma_result result;
52101
52102 if (pResampler == NULL) {
52103 return MA_INVALID_ARGS;
52104 }
52105
52106 if (sampleRateIn == 0 || sampleRateOut == 0) {
52107 return MA_INVALID_ARGS;
52108 }
52109
52110 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) {
52111 return MA_NOT_IMPLEMENTED;
52112 }
52113
52114 result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut);
52115 if (result != MA_SUCCESS) {
52116 return result;
52117 }
52118
52119 pResampler->sampleRateIn = sampleRateIn;
52120 pResampler->sampleRateOut = sampleRateOut;
52121
52122 return MA_SUCCESS;
52123 }
52124
52125 MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
52126 {
52127 ma_uint32 n;
52128 ma_uint32 d;
52129
52130 if (pResampler == NULL) {
52131 return MA_INVALID_ARGS;
52132 }
52133
52134 if (ratio <= 0) {
52135 return MA_INVALID_ARGS;
52136 }
52137
52138 d = 1000;
52139 n = (ma_uint32)(ratio * d);
52140
52141 if (n == 0) {
52142 return MA_INVALID_ARGS; /* Ratio too small. */
52143 }
52144
52145 MA_ASSERT(n != 0);
52146
52147 return ma_resampler_set_rate(pResampler, n, d);
52148 }
52149
52150 MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)
52151 {
52152 if (pResampler == NULL) {
52153 return 0;
52154 }
52155
52156 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) {
52157 return 0;
52158 }
52159
52160 return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend);
52161 }
52162
52163 MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
52164 {
52165 if (pResampler == NULL) {
52166 return 0;
52167 }
52168
52169 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) {
52170 return 0;
52171 }
52172
52173 return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend);
52174 }
52175
52176 MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
52177 {
52178 if (pInputFrameCount == NULL) {
52179 return MA_INVALID_ARGS;
52180 }
52181
52182 *pInputFrameCount = 0;
52183
52184 if (pResampler == NULL) {
52185 return MA_INVALID_ARGS;
52186 }
52187
52188 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) {
52189 return MA_NOT_IMPLEMENTED;
52190 }
52191
52192 return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount);
52193 }
52194
52195 MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
52196 {
52197 if (pOutputFrameCount == NULL) {
52198 return MA_INVALID_ARGS;
52199 }
52200
52201 *pOutputFrameCount = 0;
52202
52203 if (pResampler == NULL) {
52204 return MA_INVALID_ARGS;
52205 }
52206
52207 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) {
52208 return MA_NOT_IMPLEMENTED;
52209 }
52210
52211 return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);
52212 }
52213
52214 MA_API ma_result ma_resampler_reset(ma_resampler* pResampler)
52215 {
52216 if (pResampler == NULL) {
52217 return MA_INVALID_ARGS;
52218 }
52219
52220 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) {
52221 return MA_NOT_IMPLEMENTED;
52222 }
52223
52224 return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend);
52225 }
52226
52227 /**************************************************************************************************************************************************************
52228
52229 Channel Conversion
52230
52231 **************************************************************************************************************************************************************/
52232 #ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
52233 #define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
52234 #endif
52235
52236 #define MA_PLANE_LEFT 0
52237 #define MA_PLANE_RIGHT 1
52238 #define MA_PLANE_FRONT 2
52239 #define MA_PLANE_BACK 3
52240 #define MA_PLANE_BOTTOM 4
52241 #define MA_PLANE_TOP 5
52242
52243 static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
52244 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
52245 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
52246 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
52247 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
52248 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
52249 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
52250 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
52251 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
52252 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
52253 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
52254 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
52255 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
52256 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
52257 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
52258 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
52259 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
52260 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
52261 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
52262 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
52263 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
52264 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
52265 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
52266 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
52267 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
52268 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
52269 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
52270 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
52271 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
52272 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
52273 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
52274 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
52275 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
52276 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
52277 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
52278 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
52279 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
52280 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
52281 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
52282 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
52283 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
52284 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
52285 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
52286 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
52287 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
52288 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
52289 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
52290 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
52291 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
52292 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
52293 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
52294 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
52295 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
52296 };
52297
52298 static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
52299 {
52300 /*
52301 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
52302 the following output configuration:
52303
52304 - front/left
52305 - side/left
52306 - back/left
52307
52308 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
52309 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
52310
52311 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
52312 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
52313 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
52314 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
52315 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
52316 across 3 spatial dimensions.
52317
52318 The first thing to do is figure out how each speaker's volume is spread over each of plane:
52319 - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
52320 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
52321 - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
52322 - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
52323
52324 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
52325 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
52326 taken by the other to produce the final contribution.
52327 */
52328
52329 /* Contribution = Sum(Volume to Give * Volume to Take) */
52330 float contribution =
52331 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
52332 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
52333 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
52334 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
52335 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
52336 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
52337
52338 return contribution;
52339 }
52340
52341 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)
52342 {
52343 ma_channel_converter_config config;
52344
52345 MA_ZERO_OBJECT(&config);
52346 config.format = format;
52347 config.channelsIn = channelsIn;
52348 config.channelsOut = channelsOut;
52349 config.pChannelMapIn = pChannelMapIn;
52350 config.pChannelMapOut = pChannelMapOut;
52351 config.mixingMode = mixingMode;
52352
52353 return config;
52354 }
52355
52356 static ma_int32 ma_channel_converter_float_to_fixed(float x)
52357 {
52358 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
52359 }
52360
52361 static ma_uint32 ma_channel_map_get_spatial_channel_count(const ma_channel* pChannelMap, ma_uint32 channels)
52362 {
52363 ma_uint32 spatialChannelCount = 0;
52364 ma_uint32 iChannel;
52365
52366 MA_ASSERT(pChannelMap != NULL);
52367 MA_ASSERT(channels > 0);
52368
52369 for (iChannel = 0; iChannel < channels; ++iChannel) {
52370 if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) {
52371 spatialChannelCount++;
52372 }
52373 }
52374
52375 return spatialChannelCount;
52376 }
52377
52378 static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
52379 {
52380 int i;
52381
52382 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
52383 return MA_FALSE;
52384 }
52385
52386 if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) {
52387 return MA_FALSE;
52388 }
52389
52390 for (i = 0; i < 6; ++i) { /* Each side of a cube. */
52391 if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
52392 return MA_TRUE;
52393 }
52394 }
52395
52396 return MA_FALSE;
52397 }
52398
52399
52400 static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut)
52401 {
52402 if (channelsOut == channelsIn) {
52403 return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut);
52404 } else {
52405 return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */
52406 }
52407 }
52408
52409 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)
52410 {
52411 if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) {
52412 return ma_channel_conversion_path_passthrough;
52413 }
52414
52415 if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) {
52416 return ma_channel_conversion_path_mono_out;
52417 }
52418
52419 if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) {
52420 return ma_channel_conversion_path_mono_in;
52421 }
52422
52423 if (mode == ma_channel_mix_mode_custom_weights) {
52424 return ma_channel_conversion_path_weights;
52425 }
52426
52427 /*
52428 We can use a simple shuffle if both channel maps have the same channel count and all channel
52429 positions are present in both.
52430 */
52431 if (channelsIn == channelsOut) {
52432 ma_uint32 iChannelIn;
52433 ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
52434 for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) {
52435 ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
52436 if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) {
52437 isInputChannelPositionInOutput = MA_TRUE;
52438 break;
52439 }
52440
52441 if (!isInputChannelPositionInOutput) {
52442 areAllChannelPositionsPresent = MA_FALSE;
52443 break;
52444 }
52445 }
52446
52447 if (areAllChannelPositionsPresent) {
52448 return ma_channel_conversion_path_shuffle;
52449 }
52450 }
52451
52452 /* Getting here means we'll need to use weights. */
52453 return ma_channel_conversion_path_weights;
52454 }
52455
52456
52457 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)
52458 {
52459 ma_uint32 iChannelIn;
52460 ma_uint32 iChannelOut;
52461
52462 if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) {
52463 return MA_INVALID_ARGS;
52464 }
52465
52466 /*
52467 When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the
52468 input channel has more than one occurance of a channel position, the second one will be ignored.
52469 */
52470 for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {
52471 ma_channel channelOut;
52472
52473 /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */
52474 pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL;
52475
52476 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut);
52477 for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) {
52478 ma_channel channelIn;
52479
52480 channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn);
52481 if (channelOut == channelIn) {
52482 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
52483 break;
52484 }
52485
52486 /*
52487 Getting here means the channels don't exactly match, but we are going to support some
52488 relaxed matching for practicality. If, for example, there are two stereo channel maps,
52489 but one uses front left/right and the other uses side left/right, it makes logical
52490 sense to just map these. The way we'll do it is we'll check if there is a logical
52491 corresponding mapping, and if so, apply it, but we will *not* break from the loop,
52492 thereby giving the loop a chance to find an exact match later which will take priority.
52493 */
52494 switch (channelOut)
52495 {
52496 /* Left channels. */
52497 case MA_CHANNEL_FRONT_LEFT:
52498 case MA_CHANNEL_SIDE_LEFT:
52499 {
52500 switch (channelIn) {
52501 case MA_CHANNEL_FRONT_LEFT:
52502 case MA_CHANNEL_SIDE_LEFT:
52503 {
52504 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
52505 } break;
52506 }
52507 } break;
52508
52509 /* Right channels. */
52510 case MA_CHANNEL_FRONT_RIGHT:
52511 case MA_CHANNEL_SIDE_RIGHT:
52512 {
52513 switch (channelIn) {
52514 case MA_CHANNEL_FRONT_RIGHT:
52515 case MA_CHANNEL_SIDE_RIGHT:
52516 {
52517 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
52518 } break;
52519 }
52520 } break;
52521
52522 default: break;
52523 }
52524 }
52525 }
52526
52527 return MA_SUCCESS;
52528 }
52529
52530
52531 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)
52532 {
52533 ma_uint64 iFrame;
52534 ma_uint32 iChannelOut;
52535
52536 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52537 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52538 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52539 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52540 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52541 } else {
52542 pFramesOut[iChannelOut] = 0;
52543 }
52544 }
52545
52546 pFramesOut += channelsOut;
52547 pFramesIn += channelsIn;
52548 }
52549 }
52550
52551 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)
52552 {
52553 ma_uint64 iFrame;
52554 ma_uint32 iChannelOut;
52555
52556 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52557 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52558 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52559 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52560 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52561 } else {
52562 pFramesOut[iChannelOut] = 0;
52563 }
52564 }
52565
52566 pFramesOut += channelsOut;
52567 pFramesIn += channelsIn;
52568 }
52569 }
52570
52571 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)
52572 {
52573 ma_uint64 iFrame;
52574 ma_uint32 iChannelOut;
52575
52576 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52577 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52578 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52579 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52580 pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0];
52581 pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1];
52582 pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2];
52583 } else {
52584 pFramesOut[iChannelOut*3 + 0] = 0;
52585 } pFramesOut[iChannelOut*3 + 1] = 0;
52586 } pFramesOut[iChannelOut*3 + 2] = 0;
52587
52588 pFramesOut += channelsOut*3;
52589 pFramesIn += channelsIn*3;
52590 }
52591 }
52592
52593 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)
52594 {
52595 ma_uint64 iFrame;
52596 ma_uint32 iChannelOut;
52597
52598 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52599 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52600 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52601 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52602 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52603 } else {
52604 pFramesOut[iChannelOut] = 0;
52605 }
52606 }
52607
52608 pFramesOut += channelsOut;
52609 pFramesIn += channelsIn;
52610 }
52611 }
52612
52613 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)
52614 {
52615 ma_uint64 iFrame;
52616 ma_uint32 iChannelOut;
52617
52618 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52619 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52620 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
52621 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
52622 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
52623 } else {
52624 pFramesOut[iChannelOut] = 0;
52625 }
52626 }
52627
52628 pFramesOut += channelsOut;
52629 pFramesIn += channelsIn;
52630 }
52631 }
52632
52633 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)
52634 {
52635 if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) {
52636 return MA_INVALID_ARGS;
52637 }
52638
52639 switch (format)
52640 {
52641 case ma_format_u8:
52642 {
52643 ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52644 } break;
52645
52646 case ma_format_s16:
52647 {
52648 ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52649 } break;
52650
52651 case ma_format_s24:
52652 {
52653 ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52654 } break;
52655
52656 case ma_format_s32:
52657 {
52658 ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52659 } break;
52660
52661 case ma_format_f32:
52662 {
52663 ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable);
52664 } break;
52665
52666 default: return MA_INVALID_ARGS; /* Unknown format. */
52667 }
52668
52669 return MA_SUCCESS;
52670 }
52671
52672 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)
52673 {
52674 ma_uint64 iFrame;
52675 ma_uint32 iChannelIn;
52676 ma_uint32 accumulationCount;
52677
52678 if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) {
52679 return MA_INVALID_ARGS;
52680 }
52681
52682 /* In this case the output stream needs to be the average of all channels, ignoring NONE. */
52683
52684 /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */
52685 accumulationCount = 0;
52686 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
52687 if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) {
52688 accumulationCount += 1;
52689 }
52690 }
52691
52692 if (accumulationCount > 0) { /* <-- Prevent a division by zero. */
52693 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52694 float accumulation = 0;
52695
52696 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
52697 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
52698 if (channelIn != MA_CHANNEL_NONE) {
52699 accumulation += pFramesIn[iChannelIn];
52700 }
52701 }
52702
52703 pFramesOut[0] = accumulation / accumulationCount;
52704 pFramesOut += 1;
52705 pFramesIn += channelsIn;
52706 }
52707 } else {
52708 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1);
52709 }
52710
52711 return MA_SUCCESS;
52712 }
52713
52714 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)
52715 {
52716 ma_uint64 iFrame;
52717 ma_uint32 iChannelOut;
52718
52719 if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) {
52720 return MA_INVALID_ARGS;
52721 }
52722
52723 /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */
52724 switch (monoExpansionMode)
52725 {
52726 case ma_mono_expansion_mode_average:
52727 {
52728 float weight;
52729 ma_uint32 validChannelCount = 0;
52730
52731 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52732 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52733 if (channelOut != MA_CHANNEL_NONE) {
52734 validChannelCount += 1;
52735 }
52736 }
52737
52738 weight = 1.0f / validChannelCount;
52739
52740 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52741 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52742 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52743 if (channelOut != MA_CHANNEL_NONE) {
52744 pFramesOut[iChannelOut] = pFramesIn[0] * weight;
52745 }
52746 }
52747
52748 pFramesOut += channelsOut;
52749 pFramesIn += 1;
52750 }
52751 } break;
52752
52753 case ma_mono_expansion_mode_stereo_only:
52754 {
52755 if (channelsOut >= 2) {
52756 ma_uint32 iChannelLeft = (ma_uint32)-1;
52757 ma_uint32 iChannelRight = (ma_uint32)-1;
52758
52759 /*
52760 We first need to find our stereo channels. We prefer front-left and front-right, but
52761 if they're not available, we'll also try side-left and side-right. If neither are
52762 available we'll fall through to the default case below.
52763 */
52764 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52765 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52766 if (channelOut == MA_CHANNEL_SIDE_LEFT) {
52767 iChannelLeft = iChannelOut;
52768 }
52769 if (channelOut == MA_CHANNEL_SIDE_RIGHT) {
52770 iChannelRight = iChannelOut;
52771 }
52772 }
52773
52774 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52775 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52776 if (channelOut == MA_CHANNEL_FRONT_LEFT) {
52777 iChannelLeft = iChannelOut;
52778 }
52779 if (channelOut == MA_CHANNEL_FRONT_RIGHT) {
52780 iChannelRight = iChannelOut;
52781 }
52782 }
52783
52784
52785 if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) {
52786 /* We found our stereo channels so we can duplicate the signal across those channels. */
52787 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52788 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52789 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52790 if (channelOut != MA_CHANNEL_NONE) {
52791 if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) {
52792 pFramesOut[iChannelOut] = pFramesIn[0];
52793 } else {
52794 pFramesOut[iChannelOut] = 0.0f;
52795 }
52796 }
52797 }
52798
52799 pFramesOut += channelsOut;
52800 pFramesIn += 1;
52801 }
52802
52803 break; /* Get out of the switch. */
52804 } else {
52805 /* Fallthrough. Does not have left and right channels. */
52806 goto default_handler;
52807 }
52808 } else {
52809 /* Fallthrough. Does not have stereo channels. */
52810 goto default_handler;
52811 }
52812 }; /* Fallthrough. See comments above. */
52813
52814 case ma_mono_expansion_mode_duplicate:
52815 default:
52816 {
52817 default_handler:
52818 {
52819 if (channelsOut <= MA_MAX_CHANNELS) {
52820 ma_bool32 hasEmptyChannel = MA_FALSE;
52821 ma_channel channelPositions[MA_MAX_CHANNELS];
52822 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52823 channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52824 if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) {
52825 hasEmptyChannel = MA_TRUE;
52826 }
52827 }
52828
52829 if (hasEmptyChannel == MA_FALSE) {
52830 /*
52831 Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully
52832 help the compiler with auto-vectorization.m
52833 */
52834 if (channelsOut == 2) {
52835 #if defined(MA_SUPPORT_SSE2)
52836 if (ma_has_sse2()) {
52837 /* We want to do two frames in each iteration. */
52838 ma_uint64 unrolledFrameCount = frameCount >> 1;
52839
52840 for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
52841 __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
52842 __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
52843 _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0)));
52844 }
52845
52846 /* Tail. */
52847 iFrame = unrolledFrameCount << 1;
52848 goto generic_on_fastpath;
52849 } else
52850 #endif
52851 {
52852 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52853 for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) {
52854 pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame];
52855 }
52856 }
52857 }
52858 } else if (channelsOut == 6) {
52859 #if defined(MA_SUPPORT_SSE2)
52860 if (ma_has_sse2()) {
52861 /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */
52862 ma_uint64 unrolledFrameCount = frameCount >> 1;
52863
52864 for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
52865 __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
52866 __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
52867
52868 _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0);
52869 _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0)));
52870 _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1);
52871 }
52872
52873 /* Tail. */
52874 iFrame = unrolledFrameCount << 1;
52875 goto generic_on_fastpath;
52876 } else
52877 #endif
52878 {
52879 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52880 for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) {
52881 pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame];
52882 }
52883 }
52884 }
52885 } else if (channelsOut == 8) {
52886 #if defined(MA_SUPPORT_SSE2)
52887 if (ma_has_sse2()) {
52888 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52889 __m128 in = _mm_set1_ps(pFramesIn[iFrame]);
52890 _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in);
52891 _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in);
52892 }
52893 } else
52894 #endif
52895 {
52896 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52897 for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) {
52898 pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame];
52899 }
52900 }
52901 }
52902 } else {
52903 iFrame = 0;
52904
52905 #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */
52906 generic_on_fastpath:
52907 #endif
52908 {
52909 for (; iFrame < frameCount; iFrame += 1) {
52910 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52911 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
52912 }
52913 }
52914 }
52915 }
52916 } else {
52917 /* Slow path. Need to handle MA_CHANNEL_NONE. */
52918 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52919 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52920 if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) {
52921 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
52922 }
52923 }
52924 }
52925 }
52926 } else {
52927 /* Slow path. Too many channels to store on the stack. */
52928 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
52929 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52930 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52931 if (channelOut != MA_CHANNEL_NONE) {
52932 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
52933 }
52934 }
52935 }
52936 }
52937 }
52938 } break;
52939 }
52940
52941 return MA_SUCCESS;
52942 }
52943
52944 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)
52945 {
52946 ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode);
52947
52948 /* Optimized Path: Passthrough */
52949 if (conversionPath == ma_channel_conversion_path_passthrough) {
52950 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut);
52951 return;
52952 }
52953
52954 /* Special Path: Mono Output. */
52955 if (conversionPath == ma_channel_conversion_path_mono_out) {
52956 ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount);
52957 return;
52958 }
52959
52960 /* Special Path: Mono Input. */
52961 if (conversionPath == ma_channel_conversion_path_mono_in) {
52962 ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode);
52963 return;
52964 }
52965
52966 /* Getting here means we aren't running on an optimized conversion path. */
52967 if (channelsOut <= MA_MAX_CHANNELS) {
52968 ma_result result;
52969
52970 if (mode == ma_channel_mix_mode_simple) {
52971 ma_channel shuffleTable[MA_MAX_CHANNELS];
52972
52973 result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable);
52974 if (result != MA_SUCCESS) {
52975 return;
52976 }
52977
52978 result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32);
52979 if (result != MA_SUCCESS) {
52980 return;
52981 }
52982 } else {
52983 ma_uint32 iFrame;
52984 ma_uint32 iChannelOut;
52985 ma_uint32 iChannelIn;
52986 float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */
52987
52988 /*
52989 If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to
52990 fall back to a slower path because otherwise we'll run out of stack space.
52991 */
52992 if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) {
52993 /* Pre-compute weights. */
52994 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
52995 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
52996 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
52997 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
52998 weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
52999 }
53000 }
53001
53002 iFrame = 0;
53003
53004 /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */
53005 if (channelsOut == 8) {
53006 /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */
53007 if (channelsIn == 2) {
53008 for (; iFrame < frameCount; iFrame += 1) {
53009 float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
53010
53011 accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0];
53012 accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0];
53013 accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0];
53014 accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0];
53015 accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0];
53016 accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0];
53017 accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0];
53018 accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0];
53019
53020 accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1];
53021 accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1];
53022 accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1];
53023 accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1];
53024 accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1];
53025 accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1];
53026 accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1];
53027 accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1];
53028
53029 pFramesOut[iFrame*8 + 0] = accumulation[0];
53030 pFramesOut[iFrame*8 + 1] = accumulation[1];
53031 pFramesOut[iFrame*8 + 2] = accumulation[2];
53032 pFramesOut[iFrame*8 + 3] = accumulation[3];
53033 pFramesOut[iFrame*8 + 4] = accumulation[4];
53034 pFramesOut[iFrame*8 + 5] = accumulation[5];
53035 pFramesOut[iFrame*8 + 6] = accumulation[6];
53036 pFramesOut[iFrame*8 + 7] = accumulation[7];
53037 }
53038 } else {
53039 /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */
53040 for (; iFrame < frameCount; iFrame += 1) {
53041 float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
53042
53043 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53044 accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
53045 accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
53046 accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
53047 accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
53048 accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
53049 accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
53050 accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn];
53051 accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn];
53052 }
53053
53054 pFramesOut[iFrame*8 + 0] = accumulation[0];
53055 pFramesOut[iFrame*8 + 1] = accumulation[1];
53056 pFramesOut[iFrame*8 + 2] = accumulation[2];
53057 pFramesOut[iFrame*8 + 3] = accumulation[3];
53058 pFramesOut[iFrame*8 + 4] = accumulation[4];
53059 pFramesOut[iFrame*8 + 5] = accumulation[5];
53060 pFramesOut[iFrame*8 + 6] = accumulation[6];
53061 pFramesOut[iFrame*8 + 7] = accumulation[7];
53062 }
53063 }
53064 } else if (channelsOut == 6) {
53065 /*
53066 When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll
53067 expand our weights and do two frames at a time.
53068 */
53069 for (; iFrame < frameCount; iFrame += 1) {
53070 float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
53071
53072 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53073 accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
53074 accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
53075 accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
53076 accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
53077 accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
53078 accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
53079 }
53080
53081 pFramesOut[iFrame*6 + 0] = accumulation[0];
53082 pFramesOut[iFrame*6 + 1] = accumulation[1];
53083 pFramesOut[iFrame*6 + 2] = accumulation[2];
53084 pFramesOut[iFrame*6 + 3] = accumulation[3];
53085 pFramesOut[iFrame*6 + 4] = accumulation[4];
53086 pFramesOut[iFrame*6 + 5] = accumulation[5];
53087 }
53088 }
53089
53090 /* Leftover frames. */
53091 for (; iFrame < frameCount; iFrame += 1) {
53092 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
53093 float accumulation = 0;
53094
53095 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53096 accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn];
53097 }
53098
53099 pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
53100 }
53101 }
53102 } else {
53103 /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */
53104 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53105 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
53106 float accumulation = 0;
53107 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
53108
53109 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
53110 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
53111 accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
53112 }
53113
53114 pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
53115 }
53116 }
53117 }
53118 }
53119 } else {
53120 /* Fall back to silence. If you hit this, what are you doing with so many channels?! */
53121 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut);
53122 }
53123 }
53124
53125
53126 typedef struct
53127 {
53128 size_t sizeInBytes;
53129 size_t channelMapInOffset;
53130 size_t channelMapOutOffset;
53131 size_t shuffleTableOffset;
53132 size_t weightsOffset;
53133 } ma_channel_converter_heap_layout;
53134
53135 static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig)
53136 {
53137 return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode);
53138 }
53139
53140 static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout)
53141 {
53142 ma_channel_conversion_path conversionPath;
53143
53144 MA_ASSERT(pHeapLayout != NULL);
53145
53146 if (pConfig == NULL) {
53147 return MA_INVALID_ARGS;
53148 }
53149
53150 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
53151 return MA_INVALID_ARGS;
53152 }
53153
53154 if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) {
53155 return MA_INVALID_ARGS;
53156 }
53157
53158 if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) {
53159 return MA_INVALID_ARGS;
53160 }
53161
53162 pHeapLayout->sizeInBytes = 0;
53163
53164 /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */
53165 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
53166 if (pConfig->pChannelMapIn != NULL) {
53167 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn;
53168 }
53169
53170 /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */
53171 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
53172 if (pConfig->pChannelMapOut != NULL) {
53173 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut;
53174 }
53175
53176 /* Alignment for the next section. */
53177 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
53178
53179 /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */
53180 conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
53181
53182 /* Shuffle table */
53183 pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes;
53184 if (conversionPath == ma_channel_conversion_path_shuffle) {
53185 pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut;
53186 }
53187
53188 /* Weights */
53189 pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes;
53190 if (conversionPath == ma_channel_conversion_path_weights) {
53191 pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn;
53192 pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut;
53193 }
53194
53195 /* Make sure allocation size is aligned. */
53196 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
53197
53198 return MA_SUCCESS;
53199 }
53200
53201 MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes)
53202 {
53203 ma_result result;
53204 ma_channel_converter_heap_layout heapLayout;
53205
53206 if (pHeapSizeInBytes == NULL) {
53207 return MA_INVALID_ARGS;
53208 }
53209
53210 *pHeapSizeInBytes = 0;
53211
53212 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
53213 if (result != MA_SUCCESS) {
53214 return result;
53215 }
53216
53217 *pHeapSizeInBytes = heapLayout.sizeInBytes;
53218
53219 return MA_SUCCESS;
53220 }
53221
53222 MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter)
53223 {
53224 ma_result result;
53225 ma_channel_converter_heap_layout heapLayout;
53226
53227 if (pConverter == NULL) {
53228 return MA_INVALID_ARGS;
53229 }
53230
53231 MA_ZERO_OBJECT(pConverter);
53232
53233 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
53234 if (result != MA_SUCCESS) {
53235 return result;
53236 }
53237
53238 pConverter->_pHeap = pHeap;
53239 MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes);
53240
53241 pConverter->format = pConfig->format;
53242 pConverter->channelsIn = pConfig->channelsIn;
53243 pConverter->channelsOut = pConfig->channelsOut;
53244 pConverter->mixingMode = pConfig->mixingMode;
53245
53246 if (pConfig->pChannelMapIn != NULL) {
53247 pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
53248 ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn);
53249 } else {
53250 pConverter->pChannelMapIn = NULL; /* Use default channel map. */
53251 }
53252
53253 if (pConfig->pChannelMapOut != NULL) {
53254 pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
53255 ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
53256 } else {
53257 pConverter->pChannelMapOut = NULL; /* Use default channel map. */
53258 }
53259
53260 pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
53261
53262 if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) {
53263 pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset);
53264 ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable);
53265 }
53266
53267 if (pConverter->conversionPath == ma_channel_conversion_path_weights) {
53268 ma_uint32 iChannelIn;
53269 ma_uint32 iChannelOut;
53270
53271 if (pConverter->format == ma_format_f32) {
53272 pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset);
53273 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53274 pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn)));
53275 }
53276 } else {
53277 pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset);
53278 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53279 pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn)));
53280 }
53281 }
53282
53283 /* Silence our weights by default. */
53284 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53285 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
53286 if (pConverter->format == ma_format_f32) {
53287 pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f;
53288 } else {
53289 pConverter->weights.s16[iChannelIn][iChannelOut] = 0;
53290 }
53291 }
53292 }
53293
53294 /*
53295 We now need to fill out our weights table. This is determined by the mixing mode.
53296 */
53297
53298 /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
53299 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53300 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53301
53302 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53303 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
53304
53305 if (channelPosIn == channelPosOut) {
53306 float weight = 1;
53307
53308 if (pConverter->format == ma_format_f32) {
53309 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53310 } else {
53311 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53312 }
53313 }
53314 }
53315 }
53316
53317 switch (pConverter->mixingMode)
53318 {
53319 case ma_channel_mix_mode_custom_weights:
53320 {
53321 if (pConfig->ppWeights == NULL) {
53322 return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */
53323 }
53324
53325 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
53326 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
53327 float weight = pConfig->ppWeights[iChannelIn][iChannelOut];
53328
53329 if (pConverter->format == ma_format_f32) {
53330 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53331 } else {
53332 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53333 }
53334 }
53335 }
53336 } break;
53337
53338 case ma_channel_mix_mode_simple:
53339 {
53340 /*
53341 In simple mode, only set weights for channels that have exactly matching types, leave the rest at
53342 zero. The 1:1 mappings have already been covered before this switch statement.
53343 */
53344 } break;
53345
53346 case ma_channel_mix_mode_rectangular:
53347 default:
53348 {
53349 /* Unmapped input channels. */
53350 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53351 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53352
53353 if (ma_is_spatial_channel_position(channelPosIn)) {
53354 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) {
53355 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53356 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
53357
53358 if (ma_is_spatial_channel_position(channelPosOut)) {
53359 float weight = 0;
53360 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
53361 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
53362 }
53363
53364 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
53365 if (pConverter->format == ma_format_f32) {
53366 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
53367 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53368 }
53369 } else {
53370 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
53371 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53372 }
53373 }
53374 }
53375 }
53376 }
53377 }
53378 }
53379
53380 /* Unmapped output channels. */
53381 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53382 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
53383
53384 if (ma_is_spatial_channel_position(channelPosOut)) {
53385 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) {
53386 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53387 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53388
53389 if (ma_is_spatial_channel_position(channelPosIn)) {
53390 float weight = 0;
53391 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
53392 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
53393 }
53394
53395 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
53396 if (pConverter->format == ma_format_f32) {
53397 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
53398 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
53399 }
53400 } else {
53401 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
53402 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
53403 }
53404 }
53405 }
53406 }
53407 }
53408 }
53409 }
53410
53411 /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */
53412 if (pConfig->calculateLFEFromSpatialChannels) {
53413 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) {
53414 ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn);
53415 ma_uint32 iChannelOutLFE;
53416
53417 if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) {
53418 const float weightForLFE = 1.0f / spatialChannelCount;
53419 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53420 const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
53421 if (ma_is_spatial_channel_position(channelPosIn)) {
53422 if (pConverter->format == ma_format_f32) {
53423 if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) {
53424 pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE;
53425 }
53426 } else {
53427 if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) {
53428 pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE);
53429 }
53430 }
53431 }
53432 }
53433 }
53434 }
53435 }
53436 } break;
53437 }
53438 }
53439
53440 return MA_SUCCESS;
53441 }
53442
53443 MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter)
53444 {
53445 ma_result result;
53446 size_t heapSizeInBytes;
53447 void* pHeap;
53448
53449 result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes);
53450 if (result != MA_SUCCESS) {
53451 return result;
53452 }
53453
53454 if (heapSizeInBytes > 0) {
53455 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
53456 if (pHeap == NULL) {
53457 return MA_OUT_OF_MEMORY;
53458 }
53459 } else {
53460 pHeap = NULL;
53461 }
53462
53463 result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter);
53464 if (result != MA_SUCCESS) {
53465 ma_free(pHeap, pAllocationCallbacks);
53466 return result;
53467 }
53468
53469 pConverter->_ownsHeap = MA_TRUE;
53470 return MA_SUCCESS;
53471 }
53472
53473 MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
53474 {
53475 if (pConverter == NULL) {
53476 return;
53477 }
53478
53479 if (pConverter->_ownsHeap) {
53480 ma_free(pConverter->_pHeap, pAllocationCallbacks);
53481 }
53482 }
53483
53484 static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53485 {
53486 MA_ASSERT(pConverter != NULL);
53487 MA_ASSERT(pFramesOut != NULL);
53488 MA_ASSERT(pFramesIn != NULL);
53489
53490 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
53491 return MA_SUCCESS;
53492 }
53493
53494 static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53495 {
53496 MA_ASSERT(pConverter != NULL);
53497 MA_ASSERT(pFramesOut != NULL);
53498 MA_ASSERT(pFramesIn != NULL);
53499 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
53500
53501 return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format);
53502 }
53503
53504 static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53505 {
53506 ma_uint64 iFrame;
53507
53508 MA_ASSERT(pConverter != NULL);
53509 MA_ASSERT(pFramesOut != NULL);
53510 MA_ASSERT(pFramesIn != NULL);
53511 MA_ASSERT(pConverter->channelsIn == 1);
53512
53513 switch (pConverter->format)
53514 {
53515 case ma_format_u8:
53516 {
53517 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
53518 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
53519
53520 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53521 ma_uint32 iChannel;
53522 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53523 pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];
53524 }
53525 }
53526 } break;
53527
53528 case ma_format_s16:
53529 {
53530 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
53531 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
53532
53533 if (pConverter->channelsOut == 2) {
53534 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53535 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
53536 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
53537 }
53538 } else {
53539 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53540 ma_uint32 iChannel;
53541 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53542 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
53543 }
53544 }
53545 }
53546 } break;
53547
53548 case ma_format_s24:
53549 {
53550 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
53551 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
53552
53553 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53554 ma_uint32 iChannel;
53555 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53556 ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;
53557 ma_uint64 iSampleIn = iFrame;
53558 pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];
53559 pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];
53560 pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];
53561 }
53562 }
53563 } break;
53564
53565 case ma_format_s32:
53566 {
53567 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
53568 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
53569
53570 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53571 ma_uint32 iChannel;
53572 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53573 pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];
53574 }
53575 }
53576 } break;
53577
53578 case ma_format_f32:
53579 {
53580 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
53581 const float* pFramesInF32 = (const float*)pFramesIn;
53582
53583 if (pConverter->channelsOut == 2) {
53584 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53585 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
53586 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
53587 }
53588 } else {
53589 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53590 ma_uint32 iChannel;
53591 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
53592 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
53593 }
53594 }
53595 }
53596 } break;
53597
53598 default: return MA_INVALID_OPERATION; /* Unknown format. */
53599 }
53600
53601 return MA_SUCCESS;
53602 }
53603
53604 static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53605 {
53606 ma_uint64 iFrame;
53607 ma_uint32 iChannel;
53608
53609 MA_ASSERT(pConverter != NULL);
53610 MA_ASSERT(pFramesOut != NULL);
53611 MA_ASSERT(pFramesIn != NULL);
53612 MA_ASSERT(pConverter->channelsOut == 1);
53613
53614 switch (pConverter->format)
53615 {
53616 case ma_format_u8:
53617 {
53618 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
53619 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
53620
53621 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53622 ma_int32 t = 0;
53623 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53624 t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]);
53625 }
53626
53627 pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut);
53628 }
53629 } break;
53630
53631 case ma_format_s16:
53632 {
53633 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
53634 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
53635
53636 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53637 ma_int32 t = 0;
53638 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53639 t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel];
53640 }
53641
53642 pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn);
53643 }
53644 } break;
53645
53646 case ma_format_s24:
53647 {
53648 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
53649 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
53650
53651 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53652 ma_int64 t = 0;
53653 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53654 t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]);
53655 }
53656
53657 ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]);
53658 }
53659 } break;
53660
53661 case ma_format_s32:
53662 {
53663 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
53664 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
53665
53666 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53667 ma_int64 t = 0;
53668 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53669 t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel];
53670 }
53671
53672 pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn);
53673 }
53674 } break;
53675
53676 case ma_format_f32:
53677 {
53678 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
53679 const float* pFramesInF32 = (const float*)pFramesIn;
53680
53681 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
53682 float t = 0;
53683 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
53684 t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel];
53685 }
53686
53687 pFramesOutF32[iFrame] = t / pConverter->channelsIn;
53688 }
53689 } break;
53690
53691 default: return MA_INVALID_OPERATION; /* Unknown format. */
53692 }
53693
53694 return MA_SUCCESS;
53695 }
53696
53697 static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53698 {
53699 ma_uint32 iFrame;
53700 ma_uint32 iChannelIn;
53701 ma_uint32 iChannelOut;
53702
53703 MA_ASSERT(pConverter != NULL);
53704 MA_ASSERT(pFramesOut != NULL);
53705 MA_ASSERT(pFramesIn != NULL);
53706
53707 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
53708
53709 /* Clear. */
53710 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
53711
53712 /* Accumulate. */
53713 switch (pConverter->format)
53714 {
53715 case ma_format_u8:
53716 {
53717 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
53718 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
53719
53720 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53721 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53722 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53723 ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);
53724 ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]);
53725 ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);
53726 pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);
53727 }
53728 }
53729 }
53730 } break;
53731
53732 case ma_format_s16:
53733 {
53734 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
53735 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
53736
53737 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53738 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53739 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53740 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
53741 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
53742
53743 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
53744 }
53745 }
53746 }
53747 } break;
53748
53749 case ma_format_s24:
53750 {
53751 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
53752 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
53753
53754 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53755 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53756 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53757 ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
53758 ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]);
53759 ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);
53760 ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
53761 }
53762 }
53763 }
53764 } break;
53765
53766 case ma_format_s32:
53767 {
53768 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
53769 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
53770
53771 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53772 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53773 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53774 ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
53775 s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
53776
53777 pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
53778 }
53779 }
53780 }
53781 } break;
53782
53783 case ma_format_f32:
53784 {
53785 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
53786 const float* pFramesInF32 = (const float*)pFramesIn;
53787
53788 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
53789 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
53790 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
53791 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
53792 }
53793 }
53794 }
53795 } break;
53796
53797 default: return MA_INVALID_OPERATION; /* Unknown format. */
53798 }
53799
53800 return MA_SUCCESS;
53801 }
53802
53803 MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
53804 {
53805 if (pConverter == NULL) {
53806 return MA_INVALID_ARGS;
53807 }
53808
53809 if (pFramesOut == NULL) {
53810 return MA_INVALID_ARGS;
53811 }
53812
53813 if (pFramesIn == NULL) {
53814 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
53815 return MA_SUCCESS;
53816 }
53817
53818 switch (pConverter->conversionPath)
53819 {
53820 case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
53821 case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount);
53822 case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount);
53823 case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
53824 case ma_channel_conversion_path_weights:
53825 default:
53826 {
53827 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
53828 }
53829 }
53830 }
53831
53832 MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
53833 {
53834 if (pConverter == NULL || pChannelMap == NULL) {
53835 return MA_INVALID_ARGS;
53836 }
53837
53838 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn);
53839
53840 return MA_SUCCESS;
53841 }
53842
53843 MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
53844 {
53845 if (pConverter == NULL || pChannelMap == NULL) {
53846 return MA_INVALID_ARGS;
53847 }
53848
53849 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut);
53850
53851 return MA_SUCCESS;
53852 }
53853
53854
53855 /**************************************************************************************************************************************************************
53856
53857 Data Conversion
53858
53859 **************************************************************************************************************************************************************/
53860 MA_API ma_data_converter_config ma_data_converter_config_init_default(void)
53861 {
53862 ma_data_converter_config config;
53863 MA_ZERO_OBJECT(&config);
53864
53865 config.ditherMode = ma_dither_mode_none;
53866 config.resampling.algorithm = ma_resample_algorithm_linear;
53867 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. */
53868
53869 /* Linear resampling defaults. */
53870 config.resampling.linear.lpfOrder = 1;
53871
53872 return config;
53873 }
53874
53875 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)
53876 {
53877 ma_data_converter_config config = ma_data_converter_config_init_default();
53878 config.formatIn = formatIn;
53879 config.formatOut = formatOut;
53880 config.channelsIn = channelsIn;
53881 config.channelsOut = channelsOut;
53882 config.sampleRateIn = sampleRateIn;
53883 config.sampleRateOut = sampleRateOut;
53884
53885 return config;
53886 }
53887
53888
53889 typedef struct
53890 {
53891 size_t sizeInBytes;
53892 size_t channelConverterOffset;
53893 size_t resamplerOffset;
53894 } ma_data_converter_heap_layout;
53895
53896 static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig)
53897 {
53898 MA_ASSERT(pConfig != NULL);
53899
53900 return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;
53901 }
53902
53903 static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig)
53904 {
53905 MA_ASSERT(pConfig != NULL);
53906
53907 /*
53908 We want to avoid as much data conversion as possible. The channel converter and linear
53909 resampler both support s16 and f32 natively. We need to decide on the format to use for this
53910 stage. We call this the mid format because it's used in the middle stage of the conversion
53911 pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it
53912 will do the same thing for the input format. If it's neither we just use f32. If we are using a
53913 custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced
53914 to use that if resampling is required.
53915 */
53916 if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {
53917 return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */
53918 } else {
53919 /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) {
53920 return pConfig->formatOut;
53921 } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) {
53922 return pConfig->formatIn;
53923 } else {
53924 return ma_format_f32;
53925 }
53926 }
53927 }
53928
53929 static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
53930 {
53931 ma_channel_converter_config channelConverterConfig;
53932
53933 MA_ASSERT(pConfig != NULL);
53934
53935 channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode);
53936 channelConverterConfig.ppWeights = pConfig->ppChannelWeights;
53937 channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels;
53938
53939 return channelConverterConfig;
53940 }
53941
53942 static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
53943 {
53944 ma_resampler_config resamplerConfig;
53945 ma_uint32 resamplerChannels;
53946
53947 MA_ASSERT(pConfig != NULL);
53948
53949 /* 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. */
53950 if (pConfig->channelsIn < pConfig->channelsOut) {
53951 resamplerChannels = pConfig->channelsIn;
53952 } else {
53953 resamplerChannels = pConfig->channelsOut;
53954 }
53955
53956 resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm);
53957 resamplerConfig.linear = pConfig->resampling.linear;
53958 resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable;
53959 resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData;
53960
53961 return resamplerConfig;
53962 }
53963
53964 static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout)
53965 {
53966 ma_result result;
53967
53968 MA_ASSERT(pHeapLayout != NULL);
53969
53970 MA_ZERO_OBJECT(pHeapLayout);
53971
53972 if (pConfig == NULL) {
53973 return MA_INVALID_ARGS;
53974 }
53975
53976 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
53977 return MA_INVALID_ARGS;
53978 }
53979
53980 pHeapLayout->sizeInBytes = 0;
53981
53982 /* Channel converter. */
53983 pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes;
53984 {
53985 size_t heapSizeInBytes;
53986 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
53987
53988 result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes);
53989 if (result != MA_SUCCESS) {
53990 return result;
53991 }
53992
53993 pHeapLayout->sizeInBytes += heapSizeInBytes;
53994 }
53995
53996 /* Resampler. */
53997 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
53998 if (ma_data_converter_config_is_resampler_required(pConfig)) {
53999 size_t heapSizeInBytes;
54000 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
54001
54002 result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes);
54003 if (result != MA_SUCCESS) {
54004 return result;
54005 }
54006
54007 pHeapLayout->sizeInBytes += heapSizeInBytes;
54008 }
54009
54010 /* Make sure allocation size is aligned. */
54011 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
54012
54013 return MA_SUCCESS;
54014 }
54015
54016 MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes)
54017 {
54018 ma_result result;
54019 ma_data_converter_heap_layout heapLayout;
54020
54021 if (pHeapSizeInBytes == NULL) {
54022 return MA_INVALID_ARGS;
54023 }
54024
54025 *pHeapSizeInBytes = 0;
54026
54027 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
54028 if (result != MA_SUCCESS) {
54029 return result;
54030 }
54031
54032 *pHeapSizeInBytes = heapLayout.sizeInBytes;
54033
54034 return MA_SUCCESS;
54035 }
54036
54037 MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter)
54038 {
54039 ma_result result;
54040 ma_data_converter_heap_layout heapLayout;
54041 ma_format midFormat;
54042 ma_bool32 isResamplingRequired;
54043
54044 if (pConverter == NULL) {
54045 return MA_INVALID_ARGS;
54046 }
54047
54048 MA_ZERO_OBJECT(pConverter);
54049
54050 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
54051 if (result != MA_SUCCESS) {
54052 return result;
54053 }
54054
54055 pConverter->_pHeap = pHeap;
54056 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
54057
54058 pConverter->formatIn = pConfig->formatIn;
54059 pConverter->formatOut = pConfig->formatOut;
54060 pConverter->channelsIn = pConfig->channelsIn;
54061 pConverter->channelsOut = pConfig->channelsOut;
54062 pConverter->sampleRateIn = pConfig->sampleRateIn;
54063 pConverter->sampleRateOut = pConfig->sampleRateOut;
54064 pConverter->ditherMode = pConfig->ditherMode;
54065
54066 /*
54067 Determine if resampling is required. We need to do this so we can determine an appropriate
54068 mid format to use. If resampling is required, the mid format must be ma_format_f32 since
54069 that is the only one that is guaranteed to supported by custom resampling backends.
54070 */
54071 isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig);
54072 midFormat = ma_data_converter_config_get_mid_format(pConfig);
54073
54074
54075 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
54076 {
54077 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
54078
54079 result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter);
54080 if (result != MA_SUCCESS) {
54081 return result;
54082 }
54083
54084 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
54085 if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) {
54086 pConverter->hasChannelConverter = MA_TRUE;
54087 }
54088 }
54089
54090
54091 /* Resampler. */
54092 if (isResamplingRequired) {
54093 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
54094
54095 result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler);
54096 if (result != MA_SUCCESS) {
54097 return result;
54098 }
54099
54100 pConverter->hasResampler = MA_TRUE;
54101 }
54102
54103
54104 /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */
54105 if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
54106 /* 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. */
54107 if (pConverter->formatIn == pConverter->formatOut) {
54108 /* The formats are the same so we can just pass through. */
54109 pConverter->hasPreFormatConversion = MA_FALSE;
54110 pConverter->hasPostFormatConversion = MA_FALSE;
54111 } else {
54112 /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */
54113 pConverter->hasPreFormatConversion = MA_FALSE;
54114 pConverter->hasPostFormatConversion = MA_TRUE;
54115 }
54116 } else {
54117 /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */
54118 if (pConverter->formatIn != midFormat) {
54119 pConverter->hasPreFormatConversion = MA_TRUE;
54120 }
54121 if (pConverter->formatOut != midFormat) {
54122 pConverter->hasPostFormatConversion = MA_TRUE;
54123 }
54124 }
54125
54126 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
54127 if (pConverter->hasPreFormatConversion == MA_FALSE &&
54128 pConverter->hasPostFormatConversion == MA_FALSE &&
54129 pConverter->hasChannelConverter == MA_FALSE &&
54130 pConverter->hasResampler == MA_FALSE) {
54131 pConverter->isPassthrough = MA_TRUE;
54132 }
54133
54134
54135 /* We now need to determine our execution path. */
54136 if (pConverter->isPassthrough) {
54137 pConverter->executionPath = ma_data_converter_execution_path_passthrough;
54138 } else {
54139 if (pConverter->channelsIn < pConverter->channelsOut) {
54140 /* Do resampling first, if necessary. */
54141 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
54142
54143 if (pConverter->hasResampler) {
54144 pConverter->executionPath = ma_data_converter_execution_path_resample_first;
54145 } else {
54146 pConverter->executionPath = ma_data_converter_execution_path_channels_only;
54147 }
54148 } else {
54149 /* Do channel conversion first, if necessary. */
54150 if (pConverter->hasChannelConverter) {
54151 if (pConverter->hasResampler) {
54152 pConverter->executionPath = ma_data_converter_execution_path_channels_first;
54153 } else {
54154 pConverter->executionPath = ma_data_converter_execution_path_channels_only;
54155 }
54156 } else {
54157 /* Channel routing not required. */
54158 if (pConverter->hasResampler) {
54159 pConverter->executionPath = ma_data_converter_execution_path_resample_only;
54160 } else {
54161 pConverter->executionPath = ma_data_converter_execution_path_format_only;
54162 }
54163 }
54164 }
54165 }
54166
54167 return MA_SUCCESS;
54168 }
54169
54170 MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)
54171 {
54172 ma_result result;
54173 size_t heapSizeInBytes;
54174 void* pHeap;
54175
54176 result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes);
54177 if (result != MA_SUCCESS) {
54178 return result;
54179 }
54180
54181 if (heapSizeInBytes > 0) {
54182 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
54183 if (pHeap == NULL) {
54184 return MA_OUT_OF_MEMORY;
54185 }
54186 } else {
54187 pHeap = NULL;
54188 }
54189
54190 result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter);
54191 if (result != MA_SUCCESS) {
54192 ma_free(pHeap, pAllocationCallbacks);
54193 return result;
54194 }
54195
54196 pConverter->_ownsHeap = MA_TRUE;
54197 return MA_SUCCESS;
54198 }
54199
54200 MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
54201 {
54202 if (pConverter == NULL) {
54203 return;
54204 }
54205
54206 if (pConverter->hasResampler) {
54207 ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);
54208 }
54209
54210 ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks);
54211
54212 if (pConverter->_ownsHeap) {
54213 ma_free(pConverter->_pHeap, pAllocationCallbacks);
54214 }
54215 }
54216
54217 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)
54218 {
54219 ma_uint64 frameCountIn;
54220 ma_uint64 frameCountOut;
54221 ma_uint64 frameCount;
54222
54223 MA_ASSERT(pConverter != NULL);
54224
54225 frameCountIn = 0;
54226 if (pFrameCountIn != NULL) {
54227 frameCountIn = *pFrameCountIn;
54228 }
54229
54230 frameCountOut = 0;
54231 if (pFrameCountOut != NULL) {
54232 frameCountOut = *pFrameCountOut;
54233 }
54234
54235 frameCount = ma_min(frameCountIn, frameCountOut);
54236
54237 if (pFramesOut != NULL) {
54238 if (pFramesIn != NULL) {
54239 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54240 } else {
54241 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54242 }
54243 }
54244
54245 if (pFrameCountIn != NULL) {
54246 *pFrameCountIn = frameCount;
54247 }
54248 if (pFrameCountOut != NULL) {
54249 *pFrameCountOut = frameCount;
54250 }
54251
54252 return MA_SUCCESS;
54253 }
54254
54255 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)
54256 {
54257 ma_uint64 frameCountIn;
54258 ma_uint64 frameCountOut;
54259 ma_uint64 frameCount;
54260
54261 MA_ASSERT(pConverter != NULL);
54262
54263 frameCountIn = 0;
54264 if (pFrameCountIn != NULL) {
54265 frameCountIn = *pFrameCountIn;
54266 }
54267
54268 frameCountOut = 0;
54269 if (pFrameCountOut != NULL) {
54270 frameCountOut = *pFrameCountOut;
54271 }
54272
54273 frameCount = ma_min(frameCountIn, frameCountOut);
54274
54275 if (pFramesOut != NULL) {
54276 if (pFramesIn != NULL) {
54277 ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode);
54278 } else {
54279 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54280 }
54281 }
54282
54283 if (pFrameCountIn != NULL) {
54284 *pFrameCountIn = frameCount;
54285 }
54286 if (pFrameCountOut != NULL) {
54287 *pFrameCountOut = frameCount;
54288 }
54289
54290 return MA_SUCCESS;
54291 }
54292
54293
54294 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)
54295 {
54296 ma_result result = MA_SUCCESS;
54297 ma_uint64 frameCountIn;
54298 ma_uint64 frameCountOut;
54299 ma_uint64 framesProcessedIn;
54300 ma_uint64 framesProcessedOut;
54301
54302 MA_ASSERT(pConverter != NULL);
54303
54304 frameCountIn = 0;
54305 if (pFrameCountIn != NULL) {
54306 frameCountIn = *pFrameCountIn;
54307 }
54308
54309 frameCountOut = 0;
54310 if (pFrameCountOut != NULL) {
54311 frameCountOut = *pFrameCountOut;
54312 }
54313
54314 framesProcessedIn = 0;
54315 framesProcessedOut = 0;
54316
54317 while (framesProcessedOut < frameCountOut) {
54318 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54319 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54320 const void* pFramesInThisIteration;
54321 /* */ void* pFramesOutThisIteration;
54322 ma_uint64 frameCountInThisIteration;
54323 ma_uint64 frameCountOutThisIteration;
54324
54325 if (pFramesIn != NULL) {
54326 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54327 } else {
54328 pFramesInThisIteration = NULL;
54329 }
54330
54331 if (pFramesOut != NULL) {
54332 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54333 } else {
54334 pFramesOutThisIteration = NULL;
54335 }
54336
54337 /* Do a pre format conversion if necessary. */
54338 if (pConverter->hasPreFormatConversion) {
54339 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54340 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54341
54342 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54343 if (frameCountInThisIteration > tempBufferInCap) {
54344 frameCountInThisIteration = tempBufferInCap;
54345 }
54346
54347 if (pConverter->hasPostFormatConversion) {
54348 if (frameCountInThisIteration > tempBufferOutCap) {
54349 frameCountInThisIteration = tempBufferOutCap;
54350 }
54351 }
54352
54353 if (pFramesInThisIteration != NULL) {
54354 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54355 } else {
54356 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
54357 }
54358
54359 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54360
54361 if (pConverter->hasPostFormatConversion) {
54362 /* Both input and output conversion required. Output to the temp buffer. */
54363 if (frameCountOutThisIteration > tempBufferOutCap) {
54364 frameCountOutThisIteration = tempBufferOutCap;
54365 }
54366
54367 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
54368 } else {
54369 /* Only pre-format required. Output straight to the output buffer. */
54370 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
54371 }
54372
54373 if (result != MA_SUCCESS) {
54374 break;
54375 }
54376 } else {
54377 /* No pre-format required. Just read straight from the input buffer. */
54378 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
54379
54380 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54381 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54382 if (frameCountOutThisIteration > tempBufferOutCap) {
54383 frameCountOutThisIteration = tempBufferOutCap;
54384 }
54385
54386 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
54387 if (result != MA_SUCCESS) {
54388 break;
54389 }
54390 }
54391
54392 /* If we are doing a post format conversion we need to do that now. */
54393 if (pConverter->hasPostFormatConversion) {
54394 if (pFramesOutThisIteration != NULL) {
54395 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode);
54396 }
54397 }
54398
54399 framesProcessedIn += frameCountInThisIteration;
54400 framesProcessedOut += frameCountOutThisIteration;
54401
54402 MA_ASSERT(framesProcessedIn <= frameCountIn);
54403 MA_ASSERT(framesProcessedOut <= frameCountOut);
54404
54405 if (frameCountOutThisIteration == 0) {
54406 break; /* Consumed all of our input data. */
54407 }
54408 }
54409
54410 if (pFrameCountIn != NULL) {
54411 *pFrameCountIn = framesProcessedIn;
54412 }
54413 if (pFrameCountOut != NULL) {
54414 *pFrameCountOut = framesProcessedOut;
54415 }
54416
54417 return result;
54418 }
54419
54420 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)
54421 {
54422 MA_ASSERT(pConverter != NULL);
54423
54424 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
54425 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
54426 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54427 } else {
54428 /* Format conversion required. */
54429 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54430 }
54431 }
54432
54433 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)
54434 {
54435 ma_result result;
54436 ma_uint64 frameCountIn;
54437 ma_uint64 frameCountOut;
54438 ma_uint64 frameCount;
54439
54440 MA_ASSERT(pConverter != NULL);
54441
54442 frameCountIn = 0;
54443 if (pFrameCountIn != NULL) {
54444 frameCountIn = *pFrameCountIn;
54445 }
54446
54447 frameCountOut = 0;
54448 if (pFrameCountOut != NULL) {
54449 frameCountOut = *pFrameCountOut;
54450 }
54451
54452 frameCount = ma_min(frameCountIn, frameCountOut);
54453
54454 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
54455 /* No format conversion required. */
54456 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
54457 if (result != MA_SUCCESS) {
54458 return result;
54459 }
54460 } else {
54461 /* Format conversion required. */
54462 ma_uint64 framesProcessed = 0;
54463
54464 while (framesProcessed < frameCount) {
54465 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54466 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
54467 const void* pFramesInThisIteration;
54468 /* */ void* pFramesOutThisIteration;
54469 ma_uint64 frameCountThisIteration;
54470
54471 if (pFramesIn != NULL) {
54472 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54473 } else {
54474 pFramesInThisIteration = NULL;
54475 }
54476
54477 if (pFramesOut != NULL) {
54478 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54479 } else {
54480 pFramesOutThisIteration = NULL;
54481 }
54482
54483 /* Do a pre format conversion if necessary. */
54484 if (pConverter->hasPreFormatConversion) {
54485 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
54486 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
54487
54488 frameCountThisIteration = (frameCount - framesProcessed);
54489 if (frameCountThisIteration > tempBufferInCap) {
54490 frameCountThisIteration = tempBufferInCap;
54491 }
54492
54493 if (pConverter->hasPostFormatConversion) {
54494 if (frameCountThisIteration > tempBufferOutCap) {
54495 frameCountThisIteration = tempBufferOutCap;
54496 }
54497 }
54498
54499 if (pFramesInThisIteration != NULL) {
54500 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54501 } else {
54502 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
54503 }
54504
54505 if (pConverter->hasPostFormatConversion) {
54506 /* Both input and output conversion required. Output to the temp buffer. */
54507 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
54508 } else {
54509 /* Only pre-format required. Output straight to the output buffer. */
54510 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
54511 }
54512
54513 if (result != MA_SUCCESS) {
54514 break;
54515 }
54516 } else {
54517 /* No pre-format required. Just read straight from the input buffer. */
54518 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
54519
54520 frameCountThisIteration = (frameCount - framesProcessed);
54521 if (frameCountThisIteration > tempBufferOutCap) {
54522 frameCountThisIteration = tempBufferOutCap;
54523 }
54524
54525 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
54526 if (result != MA_SUCCESS) {
54527 break;
54528 }
54529 }
54530
54531 /* If we are doing a post format conversion we need to do that now. */
54532 if (pConverter->hasPostFormatConversion) {
54533 if (pFramesOutThisIteration != NULL) {
54534 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
54535 }
54536 }
54537
54538 framesProcessed += frameCountThisIteration;
54539 }
54540 }
54541
54542 if (pFrameCountIn != NULL) {
54543 *pFrameCountIn = frameCount;
54544 }
54545 if (pFrameCountOut != NULL) {
54546 *pFrameCountOut = frameCount;
54547 }
54548
54549 return MA_SUCCESS;
54550 }
54551
54552 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)
54553 {
54554 ma_result result;
54555 ma_uint64 frameCountIn;
54556 ma_uint64 frameCountOut;
54557 ma_uint64 framesProcessedIn;
54558 ma_uint64 framesProcessedOut;
54559 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
54560 ma_uint64 tempBufferInCap;
54561 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
54562 ma_uint64 tempBufferMidCap;
54563 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
54564 ma_uint64 tempBufferOutCap;
54565
54566 MA_ASSERT(pConverter != NULL);
54567 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
54568 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn);
54569 MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut);
54570
54571 frameCountIn = 0;
54572 if (pFrameCountIn != NULL) {
54573 frameCountIn = *pFrameCountIn;
54574 }
54575
54576 frameCountOut = 0;
54577 if (pFrameCountOut != NULL) {
54578 frameCountOut = *pFrameCountOut;
54579 }
54580
54581 framesProcessedIn = 0;
54582 framesProcessedOut = 0;
54583
54584 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54585 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54586 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
54587
54588 while (framesProcessedOut < frameCountOut) {
54589 ma_uint64 frameCountInThisIteration;
54590 ma_uint64 frameCountOutThisIteration;
54591 const void* pRunningFramesIn = NULL;
54592 void* pRunningFramesOut = NULL;
54593 const void* pResampleBufferIn;
54594 void* pChannelsBufferOut;
54595
54596 if (pFramesIn != NULL) {
54597 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54598 }
54599 if (pFramesOut != NULL) {
54600 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54601 }
54602
54603 /* Run input data through the resampler and output it to the temporary buffer. */
54604 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54605
54606 if (pConverter->hasPreFormatConversion) {
54607 if (frameCountInThisIteration > tempBufferInCap) {
54608 frameCountInThisIteration = tempBufferInCap;
54609 }
54610 }
54611
54612 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54613 if (frameCountOutThisIteration > tempBufferMidCap) {
54614 frameCountOutThisIteration = tempBufferMidCap;
54615 }
54616
54617 /* We can't read more frames than can fit in the output buffer. */
54618 if (pConverter->hasPostFormatConversion) {
54619 if (frameCountOutThisIteration > tempBufferOutCap) {
54620 frameCountOutThisIteration = tempBufferOutCap;
54621 }
54622 }
54623
54624 /* 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. */
54625
54626 /*
54627 We need to try to predict how many input frames will be required for the resampler. If the
54628 resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further
54629 off we are from this, the more wasted format conversions we'll end up doing.
54630 */
54631 #if 1
54632 {
54633 ma_uint64 requiredInputFrameCount;
54634
54635 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
54636 if (result != MA_SUCCESS) {
54637 /* Fall back to a best guess. */
54638 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
54639 }
54640
54641 if (frameCountInThisIteration > requiredInputFrameCount) {
54642 frameCountInThisIteration = requiredInputFrameCount;
54643 }
54644 }
54645 #endif
54646
54647 if (pConverter->hasPreFormatConversion) {
54648 if (pFramesIn != NULL) {
54649 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54650 pResampleBufferIn = pTempBufferIn;
54651 } else {
54652 pResampleBufferIn = NULL;
54653 }
54654 } else {
54655 pResampleBufferIn = pRunningFramesIn;
54656 }
54657
54658 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
54659 if (result != MA_SUCCESS) {
54660 return result;
54661 }
54662
54663
54664 /*
54665 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
54666 this part if we have an output buffer.
54667 */
54668 if (pFramesOut != NULL) {
54669 if (pConverter->hasPostFormatConversion) {
54670 pChannelsBufferOut = pTempBufferOut;
54671 } else {
54672 pChannelsBufferOut = pRunningFramesOut;
54673 }
54674
54675 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
54676 if (result != MA_SUCCESS) {
54677 return result;
54678 }
54679
54680 /* Finally we do post format conversion. */
54681 if (pConverter->hasPostFormatConversion) {
54682 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
54683 }
54684 }
54685
54686
54687 framesProcessedIn += frameCountInThisIteration;
54688 framesProcessedOut += frameCountOutThisIteration;
54689
54690 MA_ASSERT(framesProcessedIn <= frameCountIn);
54691 MA_ASSERT(framesProcessedOut <= frameCountOut);
54692
54693 if (frameCountOutThisIteration == 0) {
54694 break; /* Consumed all of our input data. */
54695 }
54696 }
54697
54698 if (pFrameCountIn != NULL) {
54699 *pFrameCountIn = framesProcessedIn;
54700 }
54701 if (pFrameCountOut != NULL) {
54702 *pFrameCountOut = framesProcessedOut;
54703 }
54704
54705 return MA_SUCCESS;
54706 }
54707
54708 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)
54709 {
54710 ma_result result;
54711 ma_uint64 frameCountIn;
54712 ma_uint64 frameCountOut;
54713 ma_uint64 framesProcessedIn;
54714 ma_uint64 framesProcessedOut;
54715 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
54716 ma_uint64 tempBufferInCap;
54717 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
54718 ma_uint64 tempBufferMidCap;
54719 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
54720 ma_uint64 tempBufferOutCap;
54721
54722 MA_ASSERT(pConverter != NULL);
54723 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
54724 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);
54725 MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);
54726
54727 frameCountIn = 0;
54728 if (pFrameCountIn != NULL) {
54729 frameCountIn = *pFrameCountIn;
54730 }
54731
54732 frameCountOut = 0;
54733 if (pFrameCountOut != NULL) {
54734 frameCountOut = *pFrameCountOut;
54735 }
54736
54737 framesProcessedIn = 0;
54738 framesProcessedOut = 0;
54739
54740 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
54741 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
54742 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
54743
54744 while (framesProcessedOut < frameCountOut) {
54745 ma_uint64 frameCountInThisIteration;
54746 ma_uint64 frameCountOutThisIteration;
54747 const void* pRunningFramesIn = NULL;
54748 void* pRunningFramesOut = NULL;
54749 const void* pChannelsBufferIn;
54750 void* pResampleBufferOut;
54751
54752 if (pFramesIn != NULL) {
54753 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
54754 }
54755 if (pFramesOut != NULL) {
54756 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
54757 }
54758
54759 /*
54760 Before doing any processing we need to determine how many frames we should try processing
54761 this iteration, for both input and output. The resampler requires us to perform format and
54762 channel conversion before passing any data into it. If we get our input count wrong, we'll
54763 end up peforming redundant pre-processing. This isn't the end of the world, but it does
54764 result in some inefficiencies proportionate to how far our estimates are off.
54765
54766 If the resampler has a means to calculate exactly how much we'll need, we'll use that.
54767 Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output
54768 frame count first.
54769 */
54770 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
54771 if (frameCountOutThisIteration > tempBufferMidCap) {
54772 frameCountOutThisIteration = tempBufferMidCap;
54773 }
54774
54775 if (pConverter->hasPostFormatConversion) {
54776 if (frameCountOutThisIteration > tempBufferOutCap) {
54777 frameCountOutThisIteration = tempBufferOutCap;
54778 }
54779 }
54780
54781 /* Now that we have the output frame count we can determine the input frame count. */
54782 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
54783 if (pConverter->hasPreFormatConversion) {
54784 if (frameCountInThisIteration > tempBufferInCap) {
54785 frameCountInThisIteration = tempBufferInCap;
54786 }
54787 }
54788
54789 if (frameCountInThisIteration > tempBufferMidCap) {
54790 frameCountInThisIteration = tempBufferMidCap;
54791 }
54792
54793 #if 1
54794 {
54795 ma_uint64 requiredInputFrameCount;
54796
54797 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
54798 if (result != MA_SUCCESS) {
54799 /* Fall back to a best guess. */
54800 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
54801 }
54802
54803 if (frameCountInThisIteration > requiredInputFrameCount) {
54804 frameCountInThisIteration = requiredInputFrameCount;
54805 }
54806 }
54807 #endif
54808
54809
54810 /* Pre format conversion. */
54811 if (pConverter->hasPreFormatConversion) {
54812 if (pRunningFramesIn != NULL) {
54813 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
54814 pChannelsBufferIn = pTempBufferIn;
54815 } else {
54816 pChannelsBufferIn = NULL;
54817 }
54818 } else {
54819 pChannelsBufferIn = pRunningFramesIn;
54820 }
54821
54822
54823 /* Channel conversion. */
54824 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
54825 if (result != MA_SUCCESS) {
54826 return result;
54827 }
54828
54829
54830 /* Resampling. */
54831 if (pConverter->hasPostFormatConversion) {
54832 pResampleBufferOut = pTempBufferOut;
54833 } else {
54834 pResampleBufferOut = pRunningFramesOut;
54835 }
54836
54837 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
54838 if (result != MA_SUCCESS) {
54839 return result;
54840 }
54841
54842
54843 /* Post format conversion. */
54844 if (pConverter->hasPostFormatConversion) {
54845 if (pRunningFramesOut != NULL) {
54846 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode);
54847 }
54848 }
54849
54850
54851 framesProcessedIn += frameCountInThisIteration;
54852 framesProcessedOut += frameCountOutThisIteration;
54853
54854 MA_ASSERT(framesProcessedIn <= frameCountIn);
54855 MA_ASSERT(framesProcessedOut <= frameCountOut);
54856
54857 if (frameCountOutThisIteration == 0) {
54858 break; /* Consumed all of our input data. */
54859 }
54860 }
54861
54862 if (pFrameCountIn != NULL) {
54863 *pFrameCountIn = framesProcessedIn;
54864 }
54865 if (pFrameCountOut != NULL) {
54866 *pFrameCountOut = framesProcessedOut;
54867 }
54868
54869 return MA_SUCCESS;
54870 }
54871
54872 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)
54873 {
54874 if (pConverter == NULL) {
54875 return MA_INVALID_ARGS;
54876 }
54877
54878 switch (pConverter->executionPath)
54879 {
54880 case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54881 case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54882 case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54883 case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54884 case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54885 case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
54886 default: return MA_INVALID_OPERATION; /* Should never hit this. */
54887 }
54888 }
54889
54890 MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
54891 {
54892 if (pConverter == NULL) {
54893 return MA_INVALID_ARGS;
54894 }
54895
54896 if (pConverter->hasResampler == MA_FALSE) {
54897 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
54898 }
54899
54900 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
54901 }
54902
54903 MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
54904 {
54905 if (pConverter == NULL) {
54906 return MA_INVALID_ARGS;
54907 }
54908
54909 if (pConverter->hasResampler == MA_FALSE) {
54910 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
54911 }
54912
54913 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
54914 }
54915
54916 MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter)
54917 {
54918 if (pConverter == NULL) {
54919 return 0;
54920 }
54921
54922 if (pConverter->hasResampler) {
54923 return ma_resampler_get_input_latency(&pConverter->resampler);
54924 }
54925
54926 return 0; /* No latency without a resampler. */
54927 }
54928
54929 MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter)
54930 {
54931 if (pConverter == NULL) {
54932 return 0;
54933 }
54934
54935 if (pConverter->hasResampler) {
54936 return ma_resampler_get_output_latency(&pConverter->resampler);
54937 }
54938
54939 return 0; /* No latency without a resampler. */
54940 }
54941
54942 MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
54943 {
54944 if (pInputFrameCount == NULL) {
54945 return MA_INVALID_ARGS;
54946 }
54947
54948 *pInputFrameCount = 0;
54949
54950 if (pConverter == NULL) {
54951 return MA_INVALID_ARGS;
54952 }
54953
54954 if (pConverter->hasResampler) {
54955 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount);
54956 } else {
54957 *pInputFrameCount = outputFrameCount; /* 1:1 */
54958 return MA_SUCCESS;
54959 }
54960 }
54961
54962 MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
54963 {
54964 if (pOutputFrameCount == NULL) {
54965 return MA_INVALID_ARGS;
54966 }
54967
54968 *pOutputFrameCount = 0;
54969
54970 if (pConverter == NULL) {
54971 return MA_INVALID_ARGS;
54972 }
54973
54974 if (pConverter->hasResampler) {
54975 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount);
54976 } else {
54977 *pOutputFrameCount = inputFrameCount; /* 1:1 */
54978 return MA_SUCCESS;
54979 }
54980 }
54981
54982 MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
54983 {
54984 if (pConverter == NULL || pChannelMap == NULL) {
54985 return MA_INVALID_ARGS;
54986 }
54987
54988 if (pConverter->hasChannelConverter) {
54989 ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
54990 } else {
54991 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut);
54992 }
54993
54994 return MA_SUCCESS;
54995 }
54996
54997 MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
54998 {
54999 if (pConverter == NULL || pChannelMap == NULL) {
55000 return MA_INVALID_ARGS;
55001 }
55002
55003 if (pConverter->hasChannelConverter) {
55004 ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
55005 } else {
55006 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn);
55007 }
55008
55009 return MA_SUCCESS;
55010 }
55011
55012 MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter)
55013 {
55014 if (pConverter == NULL) {
55015 return MA_INVALID_ARGS;
55016 }
55017
55018 /* There's nothing to do if we're not resampling. */
55019 if (pConverter->hasResampler == MA_FALSE) {
55020 return MA_SUCCESS;
55021 }
55022
55023 return ma_resampler_reset(&pConverter->resampler);
55024 }
55025
55026
55027
55028 /**************************************************************************************************************************************************************
55029
55030 Channel Maps
55031
55032 **************************************************************************************************************************************************************/
55033 static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
55034
55035 MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
55036 {
55037 if (pChannelMap == NULL) {
55038 return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex);
55039 } else {
55040 if (channelIndex >= channelCount) {
55041 return MA_CHANNEL_NONE;
55042 }
55043
55044 return pChannelMap[channelIndex];
55045 }
55046 }
55047
55048 MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels)
55049 {
55050 if (pChannelMap == NULL) {
55051 return;
55052 }
55053
55054 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
55055 }
55056
55057
55058 static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex)
55059 {
55060 if (channelCount == 0 || channelIndex >= channelCount) {
55061 return MA_CHANNEL_NONE;
55062 }
55063
55064 /* 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 */
55065 switch (channelCount)
55066 {
55067 case 0: return MA_CHANNEL_NONE;
55068
55069 case 1:
55070 {
55071 return MA_CHANNEL_MONO;
55072 } break;
55073
55074 case 2:
55075 {
55076 switch (channelIndex) {
55077 case 0: return MA_CHANNEL_FRONT_LEFT;
55078 case 1: return MA_CHANNEL_FRONT_RIGHT;
55079 }
55080 } break;
55081
55082 case 3: /* No defined, but best guess. */
55083 {
55084 switch (channelIndex) {
55085 case 0: return MA_CHANNEL_FRONT_LEFT;
55086 case 1: return MA_CHANNEL_FRONT_RIGHT;
55087 case 2: return MA_CHANNEL_FRONT_CENTER;
55088 }
55089 } break;
55090
55091 case 4:
55092 {
55093 switch (channelIndex) {
55094 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
55095 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
55096 case 0: return MA_CHANNEL_FRONT_LEFT;
55097 case 1: return MA_CHANNEL_FRONT_RIGHT;
55098 case 2: return MA_CHANNEL_FRONT_CENTER;
55099 case 3: return MA_CHANNEL_BACK_CENTER;
55100 #else
55101 /* Quad. */
55102 case 0: return MA_CHANNEL_FRONT_LEFT;
55103 case 1: return MA_CHANNEL_FRONT_RIGHT;
55104 case 2: return MA_CHANNEL_BACK_LEFT;
55105 case 3: return MA_CHANNEL_BACK_RIGHT;
55106 #endif
55107 }
55108 } break;
55109
55110 case 5: /* Not defined, but best guess. */
55111 {
55112 switch (channelIndex) {
55113 case 0: return MA_CHANNEL_FRONT_LEFT;
55114 case 1: return MA_CHANNEL_FRONT_RIGHT;
55115 case 2: return MA_CHANNEL_FRONT_CENTER;
55116 case 3: return MA_CHANNEL_BACK_LEFT;
55117 case 4: return MA_CHANNEL_BACK_RIGHT;
55118 }
55119 } break;
55120
55121 case 6:
55122 {
55123 switch (channelIndex) {
55124 case 0: return MA_CHANNEL_FRONT_LEFT;
55125 case 1: return MA_CHANNEL_FRONT_RIGHT;
55126 case 2: return MA_CHANNEL_FRONT_CENTER;
55127 case 3: return MA_CHANNEL_LFE;
55128 case 4: return MA_CHANNEL_SIDE_LEFT;
55129 case 5: return MA_CHANNEL_SIDE_RIGHT;
55130 }
55131 } break;
55132
55133 case 7: /* Not defined, but best guess. */
55134 {
55135 switch (channelIndex) {
55136 case 0: return MA_CHANNEL_FRONT_LEFT;
55137 case 1: return MA_CHANNEL_FRONT_RIGHT;
55138 case 2: return MA_CHANNEL_FRONT_CENTER;
55139 case 3: return MA_CHANNEL_LFE;
55140 case 4: return MA_CHANNEL_BACK_CENTER;
55141 case 5: return MA_CHANNEL_SIDE_LEFT;
55142 case 6: return MA_CHANNEL_SIDE_RIGHT;
55143 }
55144 } break;
55145
55146 case 8:
55147 default:
55148 {
55149 switch (channelIndex) {
55150 case 0: return MA_CHANNEL_FRONT_LEFT;
55151 case 1: return MA_CHANNEL_FRONT_RIGHT;
55152 case 2: return MA_CHANNEL_FRONT_CENTER;
55153 case 3: return MA_CHANNEL_LFE;
55154 case 4: return MA_CHANNEL_BACK_LEFT;
55155 case 5: return MA_CHANNEL_BACK_RIGHT;
55156 case 6: return MA_CHANNEL_SIDE_LEFT;
55157 case 7: return MA_CHANNEL_SIDE_RIGHT;
55158 }
55159 } break;
55160 }
55161
55162 if (channelCount > 8) {
55163 if (channelIndex < 32) { /* We have 32 AUX channels. */
55164 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55165 }
55166 }
55167
55168 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55169 return MA_CHANNEL_NONE;
55170 }
55171
55172 static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex)
55173 {
55174 switch (channelCount)
55175 {
55176 case 0: return MA_CHANNEL_NONE;
55177
55178 case 1:
55179 {
55180 return MA_CHANNEL_MONO;
55181 } break;
55182
55183 case 2:
55184 {
55185 switch (channelIndex) {
55186 case 0: return MA_CHANNEL_FRONT_LEFT;
55187 case 1: return MA_CHANNEL_FRONT_RIGHT;
55188 }
55189 } break;
55190
55191 case 3:
55192 {
55193 switch (channelIndex) {
55194 case 0: return MA_CHANNEL_FRONT_LEFT;
55195 case 1: return MA_CHANNEL_FRONT_RIGHT;
55196 case 2: return MA_CHANNEL_FRONT_CENTER;
55197 }
55198 } break;
55199
55200 case 4:
55201 {
55202 switch (channelIndex) {
55203 case 0: return MA_CHANNEL_FRONT_LEFT;
55204 case 1: return MA_CHANNEL_FRONT_RIGHT;
55205 case 2: return MA_CHANNEL_BACK_LEFT;
55206 case 3: return MA_CHANNEL_BACK_RIGHT;
55207 }
55208 } break;
55209
55210 case 5:
55211 {
55212 switch (channelIndex) {
55213 case 0: return MA_CHANNEL_FRONT_LEFT;
55214 case 1: return MA_CHANNEL_FRONT_RIGHT;
55215 case 2: return MA_CHANNEL_BACK_LEFT;
55216 case 3: return MA_CHANNEL_BACK_RIGHT;
55217 case 4: return MA_CHANNEL_FRONT_CENTER;
55218 }
55219 } break;
55220
55221 case 6:
55222 {
55223 switch (channelIndex) {
55224 case 0: return MA_CHANNEL_FRONT_LEFT;
55225 case 1: return MA_CHANNEL_FRONT_RIGHT;
55226 case 2: return MA_CHANNEL_BACK_LEFT;
55227 case 3: return MA_CHANNEL_BACK_RIGHT;
55228 case 4: return MA_CHANNEL_FRONT_CENTER;
55229 case 5: return MA_CHANNEL_LFE;
55230 }
55231 } break;
55232
55233 case 7:
55234 {
55235 switch (channelIndex) {
55236 case 0: return MA_CHANNEL_FRONT_LEFT;
55237 case 1: return MA_CHANNEL_FRONT_RIGHT;
55238 case 2: return MA_CHANNEL_BACK_LEFT;
55239 case 3: return MA_CHANNEL_BACK_RIGHT;
55240 case 4: return MA_CHANNEL_FRONT_CENTER;
55241 case 5: return MA_CHANNEL_LFE;
55242 case 6: return MA_CHANNEL_BACK_CENTER;
55243 }
55244 } break;
55245
55246 case 8:
55247 default:
55248 {
55249 switch (channelIndex) {
55250 case 0: return MA_CHANNEL_FRONT_LEFT;
55251 case 1: return MA_CHANNEL_FRONT_RIGHT;
55252 case 2: return MA_CHANNEL_BACK_LEFT;
55253 case 3: return MA_CHANNEL_BACK_RIGHT;
55254 case 4: return MA_CHANNEL_FRONT_CENTER;
55255 case 5: return MA_CHANNEL_LFE;
55256 case 6: return MA_CHANNEL_SIDE_LEFT;
55257 case 7: return MA_CHANNEL_SIDE_RIGHT;
55258 }
55259 } break;
55260 }
55261
55262 if (channelCount > 8) {
55263 if (channelIndex < 32) { /* We have 32 AUX channels. */
55264 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55265 }
55266 }
55267
55268 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55269 return MA_CHANNEL_NONE;
55270 }
55271
55272 static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex)
55273 {
55274 switch (channelCount)
55275 {
55276 case 0: return MA_CHANNEL_NONE;
55277
55278 case 1:
55279 {
55280 return MA_CHANNEL_MONO;
55281 } break;
55282
55283 case 2:
55284 {
55285 switch (channelIndex) {
55286 case 0: return MA_CHANNEL_FRONT_LEFT;
55287 case 1: return MA_CHANNEL_FRONT_RIGHT;
55288 }
55289 } break;
55290
55291 case 3:
55292 {
55293 switch (channelIndex) {
55294 case 0: return MA_CHANNEL_FRONT_LEFT;
55295 case 1: return MA_CHANNEL_FRONT_RIGHT;
55296 case 2: return MA_CHANNEL_FRONT_CENTER;
55297 }
55298 } break;
55299
55300 case 4:
55301 {
55302 switch (channelIndex) {
55303 case 0: return MA_CHANNEL_FRONT_LEFT;
55304 case 2: return MA_CHANNEL_FRONT_CENTER;
55305 case 1: return MA_CHANNEL_FRONT_RIGHT;
55306 case 3: return MA_CHANNEL_BACK_CENTER;
55307 }
55308 } break;
55309
55310 case 5:
55311 {
55312 switch (channelIndex) {
55313 case 0: return MA_CHANNEL_FRONT_LEFT;
55314 case 1: return MA_CHANNEL_FRONT_RIGHT;
55315 case 2: return MA_CHANNEL_FRONT_CENTER;
55316 case 3: return MA_CHANNEL_BACK_LEFT;
55317 case 4: return MA_CHANNEL_BACK_RIGHT;
55318 }
55319 } break;
55320
55321 case 6:
55322 default:
55323 {
55324 switch (channelIndex) {
55325 case 0: return MA_CHANNEL_FRONT_LEFT;
55326 case 1: return MA_CHANNEL_SIDE_LEFT;
55327 case 2: return MA_CHANNEL_FRONT_CENTER;
55328 case 3: return MA_CHANNEL_FRONT_RIGHT;
55329 case 4: return MA_CHANNEL_SIDE_RIGHT;
55330 case 5: return MA_CHANNEL_BACK_CENTER;
55331 }
55332 } break;
55333 }
55334
55335 if (channelCount > 6) {
55336 if (channelIndex < 32) { /* We have 32 AUX channels. */
55337 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
55338 }
55339 }
55340
55341 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55342 return MA_CHANNEL_NONE;
55343 }
55344
55345 static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex)
55346 {
55347 switch (channelCount)
55348 {
55349 case 0: return MA_CHANNEL_NONE;
55350
55351 case 1:
55352 {
55353 return MA_CHANNEL_MONO;
55354 } break;
55355
55356 case 2:
55357 {
55358 switch (channelIndex) {
55359 case 0: return MA_CHANNEL_FRONT_LEFT;
55360 case 1: return MA_CHANNEL_FRONT_RIGHT;
55361 }
55362 } break;
55363
55364 case 3:
55365 {
55366 switch (channelIndex) {
55367 case 0: return MA_CHANNEL_FRONT_LEFT;
55368 case 1: return MA_CHANNEL_FRONT_RIGHT;
55369 case 2: return MA_CHANNEL_FRONT_CENTER;
55370 }
55371 } break;
55372
55373 case 4:
55374 {
55375 switch (channelIndex) {
55376 case 0: return MA_CHANNEL_FRONT_LEFT;
55377 case 1: return MA_CHANNEL_FRONT_RIGHT;
55378 case 2: return MA_CHANNEL_BACK_LEFT;
55379 case 3: return MA_CHANNEL_BACK_RIGHT;
55380 }
55381 } break;
55382
55383 case 5:
55384 {
55385 switch (channelIndex) {
55386 case 0: return MA_CHANNEL_FRONT_LEFT;
55387 case 1: return MA_CHANNEL_FRONT_RIGHT;
55388 case 2: return MA_CHANNEL_FRONT_CENTER;
55389 case 3: return MA_CHANNEL_BACK_LEFT;
55390 case 4: return MA_CHANNEL_BACK_RIGHT;
55391 }
55392 } break;
55393
55394 case 6:
55395 {
55396 switch (channelIndex) {
55397 case 0: return MA_CHANNEL_FRONT_LEFT;
55398 case 1: return MA_CHANNEL_FRONT_RIGHT;
55399 case 2: return MA_CHANNEL_FRONT_CENTER;
55400 case 3: return MA_CHANNEL_LFE;
55401 case 4: return MA_CHANNEL_BACK_LEFT;
55402 case 5: return MA_CHANNEL_BACK_RIGHT;
55403 }
55404 } break;
55405
55406 case 7:
55407 {
55408 switch (channelIndex) {
55409 case 0: return MA_CHANNEL_FRONT_LEFT;
55410 case 1: return MA_CHANNEL_FRONT_RIGHT;
55411 case 2: return MA_CHANNEL_FRONT_CENTER;
55412 case 3: return MA_CHANNEL_LFE;
55413 case 4: return MA_CHANNEL_BACK_CENTER;
55414 case 5: return MA_CHANNEL_SIDE_LEFT;
55415 case 6: return MA_CHANNEL_SIDE_RIGHT;
55416 }
55417 } break;
55418
55419 case 8:
55420 default:
55421 {
55422 switch (channelIndex) {
55423 case 0: return MA_CHANNEL_FRONT_LEFT;
55424 case 1: return MA_CHANNEL_FRONT_RIGHT;
55425 case 2: return MA_CHANNEL_FRONT_CENTER;
55426 case 3: return MA_CHANNEL_LFE;
55427 case 4: return MA_CHANNEL_BACK_LEFT;
55428 case 5: return MA_CHANNEL_BACK_RIGHT;
55429 case 6: return MA_CHANNEL_SIDE_LEFT;
55430 case 7: return MA_CHANNEL_SIDE_RIGHT;
55431 }
55432 } break;
55433 }
55434
55435 if (channelCount > 8) {
55436 if (channelIndex < 32) { /* We have 32 AUX channels. */
55437 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55438 }
55439 }
55440
55441 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55442 return MA_CHANNEL_NONE;
55443 }
55444
55445 static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex)
55446 {
55447 switch (channelCount)
55448 {
55449 case 0: return MA_CHANNEL_NONE;
55450
55451 case 1:
55452 {
55453 return MA_CHANNEL_MONO;
55454 } break;
55455
55456 case 2:
55457 {
55458 switch (channelIndex) {
55459 case 0: return MA_CHANNEL_FRONT_LEFT;
55460 case 1: return MA_CHANNEL_FRONT_RIGHT;
55461 }
55462 } break;
55463
55464 case 3:
55465 {
55466 switch (channelIndex) {
55467 case 0: return MA_CHANNEL_FRONT_LEFT;
55468 case 1: return MA_CHANNEL_FRONT_CENTER;
55469 case 2: return MA_CHANNEL_FRONT_RIGHT;
55470 }
55471 } break;
55472
55473 case 4:
55474 {
55475 switch (channelIndex) {
55476 case 0: return MA_CHANNEL_FRONT_LEFT;
55477 case 1: return MA_CHANNEL_FRONT_RIGHT;
55478 case 2: return MA_CHANNEL_BACK_LEFT;
55479 case 3: return MA_CHANNEL_BACK_RIGHT;
55480 }
55481 } break;
55482
55483 case 5:
55484 {
55485 switch (channelIndex) {
55486 case 0: return MA_CHANNEL_FRONT_LEFT;
55487 case 1: return MA_CHANNEL_FRONT_CENTER;
55488 case 2: return MA_CHANNEL_FRONT_RIGHT;
55489 case 3: return MA_CHANNEL_BACK_LEFT;
55490 case 4: return MA_CHANNEL_BACK_RIGHT;
55491 }
55492 } break;
55493
55494 case 6:
55495 {
55496 switch (channelIndex) {
55497 case 0: return MA_CHANNEL_FRONT_LEFT;
55498 case 1: return MA_CHANNEL_FRONT_CENTER;
55499 case 2: return MA_CHANNEL_FRONT_RIGHT;
55500 case 3: return MA_CHANNEL_BACK_LEFT;
55501 case 4: return MA_CHANNEL_BACK_RIGHT;
55502 case 5: return MA_CHANNEL_LFE;
55503 }
55504 } break;
55505
55506 case 7:
55507 {
55508 switch (channelIndex) {
55509 case 0: return MA_CHANNEL_FRONT_LEFT;
55510 case 1: return MA_CHANNEL_FRONT_CENTER;
55511 case 2: return MA_CHANNEL_FRONT_RIGHT;
55512 case 3: return MA_CHANNEL_SIDE_LEFT;
55513 case 4: return MA_CHANNEL_SIDE_RIGHT;
55514 case 5: return MA_CHANNEL_BACK_CENTER;
55515 case 6: return MA_CHANNEL_LFE;
55516 }
55517 } break;
55518
55519 case 8:
55520 default:
55521 {
55522 switch (channelIndex) {
55523 case 0: return MA_CHANNEL_FRONT_LEFT;
55524 case 1: return MA_CHANNEL_FRONT_CENTER;
55525 case 2: return MA_CHANNEL_FRONT_RIGHT;
55526 case 3: return MA_CHANNEL_SIDE_LEFT;
55527 case 4: return MA_CHANNEL_SIDE_RIGHT;
55528 case 5: return MA_CHANNEL_BACK_LEFT;
55529 case 6: return MA_CHANNEL_BACK_RIGHT;
55530 case 7: return MA_CHANNEL_LFE;
55531 }
55532 } break;
55533 }
55534
55535 if (channelCount > 8) {
55536 if (channelIndex < 32) { /* We have 32 AUX channels. */
55537 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55538 }
55539 }
55540
55541 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55542 return MA_CHANNEL_NONE;
55543 }
55544
55545 static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex)
55546 {
55547 switch (channelCount)
55548 {
55549 case 0: return MA_CHANNEL_NONE;
55550
55551 case 1:
55552 {
55553 return MA_CHANNEL_MONO;
55554 } break;
55555
55556 case 2:
55557 {
55558 switch (channelIndex) {
55559 case 0: return MA_CHANNEL_FRONT_LEFT;
55560 case 1: return MA_CHANNEL_FRONT_RIGHT;
55561 }
55562 } break;
55563
55564 case 3:
55565 {
55566 switch (channelIndex) {
55567 case 0: return MA_CHANNEL_FRONT_LEFT;
55568 case 1: return MA_CHANNEL_FRONT_RIGHT;
55569 case 2: return MA_CHANNEL_FRONT_CENTER;
55570 }
55571 } break;
55572
55573 case 4:
55574 {
55575 switch (channelIndex) {
55576 case 0: return MA_CHANNEL_FRONT_LEFT;
55577 case 1: return MA_CHANNEL_FRONT_RIGHT;
55578 case 2: return MA_CHANNEL_BACK_LEFT;
55579 case 3: return MA_CHANNEL_BACK_RIGHT;
55580 }
55581 } break;
55582
55583 case 5:
55584 {
55585 switch (channelIndex) {
55586 case 0: return MA_CHANNEL_FRONT_LEFT;
55587 case 1: return MA_CHANNEL_FRONT_RIGHT;
55588 case 2: return MA_CHANNEL_FRONT_CENTER;
55589 case 3: return MA_CHANNEL_BACK_LEFT;
55590 case 4: return MA_CHANNEL_BACK_RIGHT;
55591 }
55592 } break;
55593
55594 case 6:
55595 {
55596 switch (channelIndex) {
55597 case 0: return MA_CHANNEL_FRONT_LEFT;
55598 case 1: return MA_CHANNEL_FRONT_CENTER;
55599 case 2: return MA_CHANNEL_FRONT_RIGHT;
55600 case 3: return MA_CHANNEL_BACK_LEFT;
55601 case 4: return MA_CHANNEL_BACK_RIGHT;
55602 case 5: return MA_CHANNEL_LFE;
55603 }
55604 } break;
55605
55606 case 7:
55607 {
55608 switch (channelIndex) {
55609 case 0: return MA_CHANNEL_FRONT_LEFT;
55610 case 1: return MA_CHANNEL_FRONT_CENTER;
55611 case 2: return MA_CHANNEL_FRONT_RIGHT;
55612 case 3: return MA_CHANNEL_SIDE_LEFT;
55613 case 4: return MA_CHANNEL_SIDE_RIGHT;
55614 case 5: return MA_CHANNEL_BACK_CENTER;
55615 case 6: return MA_CHANNEL_LFE;
55616 }
55617 } break;
55618
55619 case 8:
55620 default:
55621 {
55622 switch (channelIndex) {
55623 case 0: return MA_CHANNEL_FRONT_LEFT;
55624 case 1: return MA_CHANNEL_FRONT_CENTER;
55625 case 2: return MA_CHANNEL_FRONT_RIGHT;
55626 case 3: return MA_CHANNEL_SIDE_LEFT;
55627 case 4: return MA_CHANNEL_SIDE_RIGHT;
55628 case 5: return MA_CHANNEL_BACK_LEFT;
55629 case 6: return MA_CHANNEL_BACK_RIGHT;
55630 case 7: return MA_CHANNEL_LFE;
55631 }
55632 } break;
55633 }
55634
55635 if (channelCount > 8) {
55636 if (channelIndex < 32) { /* We have 32 AUX channels. */
55637 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
55638 }
55639 }
55640
55641 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55642 return MA_CHANNEL_NONE;
55643 }
55644
55645 static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex)
55646 {
55647 switch (channelCount)
55648 {
55649 case 0: return MA_CHANNEL_NONE;
55650
55651 case 1:
55652 {
55653 return MA_CHANNEL_MONO;
55654 } break;
55655
55656 case 2:
55657 {
55658 switch (channelIndex) {
55659 case 0: return MA_CHANNEL_FRONT_LEFT;
55660 case 1: return MA_CHANNEL_FRONT_RIGHT;
55661 }
55662 } break;
55663
55664 case 3: /* No defined, but best guess. */
55665 {
55666 switch (channelIndex) {
55667 case 0: return MA_CHANNEL_FRONT_LEFT;
55668 case 1: return MA_CHANNEL_FRONT_RIGHT;
55669 case 2: return MA_CHANNEL_FRONT_CENTER;
55670 }
55671 } break;
55672
55673 case 4:
55674 {
55675 switch (channelIndex) {
55676 case 0: return MA_CHANNEL_FRONT_LEFT;
55677 case 1: return MA_CHANNEL_FRONT_RIGHT;
55678 case 2: return MA_CHANNEL_BACK_LEFT;
55679 case 3: return MA_CHANNEL_BACK_RIGHT;
55680 }
55681 } break;
55682
55683 case 5: /* Not defined, but best guess. */
55684 {
55685 switch (channelIndex) {
55686 case 0: return MA_CHANNEL_FRONT_LEFT;
55687 case 1: return MA_CHANNEL_FRONT_RIGHT;
55688 case 2: return MA_CHANNEL_BACK_LEFT;
55689 case 3: return MA_CHANNEL_BACK_RIGHT;
55690 case 4: return MA_CHANNEL_FRONT_CENTER;
55691 }
55692 } break;
55693
55694 case 6:
55695 default:
55696 {
55697 switch (channelIndex) {
55698 case 0: return MA_CHANNEL_FRONT_LEFT;
55699 case 1: return MA_CHANNEL_FRONT_RIGHT;
55700 case 2: return MA_CHANNEL_BACK_LEFT;
55701 case 3: return MA_CHANNEL_BACK_RIGHT;
55702 case 4: return MA_CHANNEL_FRONT_CENTER;
55703 case 5: return MA_CHANNEL_LFE;
55704 }
55705 } break;
55706 }
55707
55708 if (channelCount > 6) {
55709 if (channelIndex < 32) { /* We have 32 AUX channels. */
55710 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
55711 }
55712 }
55713
55714 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
55715 return MA_CHANNEL_NONE;
55716 }
55717
55718
55719 static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
55720 {
55721 if (channelCount == 0 || channelIndex >= channelCount) {
55722 return MA_CHANNEL_NONE;
55723 }
55724
55725 switch (standardChannelMap)
55726 {
55727 case ma_standard_channel_map_alsa:
55728 {
55729 return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex);
55730 } break;
55731
55732 case ma_standard_channel_map_rfc3551:
55733 {
55734 return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex);
55735 } break;
55736
55737 case ma_standard_channel_map_flac:
55738 {
55739 return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex);
55740 } break;
55741
55742 case ma_standard_channel_map_vorbis:
55743 {
55744 return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex);
55745 } break;
55746
55747 case ma_standard_channel_map_sound4:
55748 {
55749 return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex);
55750 } break;
55751
55752 case ma_standard_channel_map_sndio:
55753 {
55754 return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex);
55755 } break;
55756
55757 case ma_standard_channel_map_microsoft: /* Also default. */
55758 /*case ma_standard_channel_map_default;*/
55759 default:
55760 {
55761 return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex);
55762 } break;
55763 }
55764 }
55765
55766 MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels)
55767 {
55768 ma_uint32 iChannel;
55769
55770 if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) {
55771 return;
55772 }
55773
55774 for (iChannel = 0; iChannel < channels; iChannel += 1) {
55775 if (channelMapCap == 0) {
55776 break; /* Ran out of room. */
55777 }
55778
55779 pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel);
55780 pChannelMap += 1;
55781 channelMapCap -= 1;
55782 }
55783 }
55784
55785 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
55786 {
55787 if (pOut != NULL && pIn != NULL && channels > 0) {
55788 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
55789 }
55790 }
55791
55792 MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels)
55793 {
55794 if (pOut == NULL || channels == 0) {
55795 return;
55796 }
55797
55798 if (pIn != NULL) {
55799 ma_channel_map_copy(pOut, pIn, channels);
55800 } else {
55801 ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels);
55802 }
55803 }
55804
55805 MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels)
55806 {
55807 /* A channel count of 0 is invalid. */
55808 if (channels == 0) {
55809 return MA_FALSE;
55810 }
55811
55812 /* It does not make sense to have a mono channel when there is more than 1 channel. */
55813 if (channels > 1) {
55814 ma_uint32 iChannel;
55815 for (iChannel = 0; iChannel < channels; ++iChannel) {
55816 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) {
55817 return MA_FALSE;
55818 }
55819 }
55820 }
55821
55822 return MA_TRUE;
55823 }
55824
55825 MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels)
55826 {
55827 ma_uint32 iChannel;
55828
55829 if (pChannelMapA == pChannelMapB) {
55830 return MA_TRUE;
55831 }
55832
55833 for (iChannel = 0; iChannel < channels; ++iChannel) {
55834 if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) {
55835 return MA_FALSE;
55836 }
55837 }
55838
55839 return MA_TRUE;
55840 }
55841
55842 MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels)
55843 {
55844 ma_uint32 iChannel;
55845
55846 /* A null channel map is equivalent to the default channel map. */
55847 if (pChannelMap == NULL) {
55848 return MA_FALSE;
55849 }
55850
55851 for (iChannel = 0; iChannel < channels; ++iChannel) {
55852 if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
55853 return MA_FALSE;
55854 }
55855 }
55856
55857 return MA_TRUE;
55858 }
55859
55860 MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
55861 {
55862 return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL);
55863 }
55864
55865 MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex)
55866 {
55867 ma_uint32 iChannel;
55868
55869 if (pChannelIndex != NULL) {
55870 *pChannelIndex = (ma_uint32)-1;
55871 }
55872
55873 for (iChannel = 0; iChannel < channels; ++iChannel) {
55874 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) {
55875 if (pChannelIndex != NULL) {
55876 *pChannelIndex = iChannel;
55877 }
55878
55879 return MA_TRUE;
55880 }
55881 }
55882
55883 /* Getting here means the channel position was not found. */
55884 return MA_FALSE;
55885 }
55886
55887 MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap)
55888 {
55889 size_t len;
55890 ma_uint32 iChannel;
55891
55892 len = 0;
55893
55894 for (iChannel = 0; iChannel < channels; iChannel += 1) {
55895 const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel));
55896 size_t channelStrLen = strlen(pChannelStr);
55897
55898 /* Append the string if necessary. */
55899 if (pBufferOut != NULL && bufferCap > len + channelStrLen) {
55900 MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen);
55901 }
55902 len += channelStrLen;
55903
55904 /* Append a space if it's not the last item. */
55905 if (iChannel+1 < channels) {
55906 if (pBufferOut != NULL && bufferCap > len + 1) {
55907 pBufferOut[len] = ' ';
55908 }
55909 len += 1;
55910 }
55911 }
55912
55913 /* Null terminate. Don't increment the length here. */
55914 if (pBufferOut != NULL && bufferCap > len + 1) {
55915 pBufferOut[len] = '\0';
55916 }
55917
55918 return len;
55919 }
55920
55921 MA_API const char* ma_channel_position_to_string(ma_channel channel)
55922 {
55923 switch (channel)
55924 {
55925 case MA_CHANNEL_NONE : return "CHANNEL_NONE";
55926 case MA_CHANNEL_MONO : return "CHANNEL_MONO";
55927 case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT";
55928 case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT";
55929 case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER";
55930 case MA_CHANNEL_LFE : return "CHANNEL_LFE";
55931 case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT";
55932 case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT";
55933 case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER ";
55934 case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER";
55935 case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER";
55936 case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT";
55937 case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT";
55938 case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER";
55939 case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT";
55940 case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER";
55941 case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT";
55942 case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT";
55943 case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER";
55944 case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT";
55945 case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0";
55946 case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1";
55947 case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2";
55948 case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3";
55949 case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4";
55950 case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5";
55951 case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6";
55952 case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7";
55953 case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8";
55954 case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9";
55955 case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10";
55956 case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11";
55957 case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12";
55958 case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13";
55959 case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14";
55960 case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15";
55961 case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16";
55962 case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17";
55963 case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18";
55964 case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19";
55965 case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20";
55966 case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21";
55967 case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22";
55968 case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23";
55969 case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24";
55970 case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25";
55971 case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26";
55972 case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27";
55973 case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28";
55974 case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29";
55975 case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30";
55976 case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31";
55977 default: break;
55978 }
55979
55980 return "UNKNOWN";
55981 }
55982
55983
55984
55985 /**************************************************************************************************************************************************************
55986
55987 Conversion Helpers
55988
55989 **************************************************************************************************************************************************************/
55990 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)
55991 {
55992 ma_data_converter_config config;
55993
55994 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
55995 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
55996
55997 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
55998 }
55999
56000 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)
56001 {
56002 ma_result result;
56003 ma_data_converter converter;
56004
56005 if (frameCountIn == 0 || pConfig == NULL) {
56006 return 0;
56007 }
56008
56009 result = ma_data_converter_init(pConfig, NULL, &converter);
56010 if (result != MA_SUCCESS) {
56011 return 0; /* Failed to initialize the data converter. */
56012 }
56013
56014 if (pOut == NULL) {
56015 result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut);
56016 if (result != MA_SUCCESS) {
56017 if (result == MA_NOT_IMPLEMENTED) {
56018 /* No way to calculate the number of frames, so we'll need to brute force it and loop. */
56019 frameCountOut = 0;
56020
56021 while (frameCountIn > 0) {
56022 ma_uint64 framesProcessedIn = frameCountIn;
56023 ma_uint64 framesProcessedOut = 0xFFFFFFFF;
56024
56025 result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut);
56026 if (result != MA_SUCCESS) {
56027 break;
56028 }
56029
56030 frameCountIn -= framesProcessedIn;
56031 }
56032 }
56033 }
56034 } else {
56035 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
56036 if (result != MA_SUCCESS) {
56037 frameCountOut = 0;
56038 }
56039 }
56040
56041 ma_data_converter_uninit(&converter, NULL);
56042 return frameCountOut;
56043 }
56044
56045
56046 /**************************************************************************************************************************************************************
56047
56048 Ring Buffer
56049
56050 **************************************************************************************************************************************************************/
56051 static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
56052 {
56053 return encodedOffset & 0x7FFFFFFF;
56054 }
56055
56056 static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
56057 {
56058 return encodedOffset & 0x80000000;
56059 }
56060
56061 static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
56062 {
56063 MA_ASSERT(pRB != NULL);
56064 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset)));
56065 }
56066
56067 static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
56068 {
56069 MA_ASSERT(pRB != NULL);
56070 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset)));
56071 }
56072
56073 static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
56074 {
56075 return offsetLoopFlag | offsetInBytes;
56076 }
56077
56078 static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
56079 {
56080 MA_ASSERT(pOffsetInBytes != NULL);
56081 MA_ASSERT(pOffsetLoopFlag != NULL);
56082
56083 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
56084 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
56085 }
56086
56087
56088 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)
56089 {
56090 ma_result result;
56091 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
56092
56093 if (pRB == NULL) {
56094 return MA_INVALID_ARGS;
56095 }
56096
56097 if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
56098 return MA_INVALID_ARGS;
56099 }
56100
56101 if (subbufferSizeInBytes > maxSubBufferSize) {
56102 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
56103 }
56104
56105
56106 MA_ZERO_OBJECT(pRB);
56107
56108 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
56109 if (result != MA_SUCCESS) {
56110 return result;
56111 }
56112
56113 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
56114 pRB->subbufferCount = (ma_uint32)subbufferCount;
56115
56116 if (pOptionalPreallocatedBuffer != NULL) {
56117 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
56118 pRB->pBuffer = pOptionalPreallocatedBuffer;
56119 } else {
56120 size_t bufferSizeInBytes;
56121
56122 /*
56123 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
56124 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
56125 */
56126 pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT;
56127
56128 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
56129 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
56130 if (pRB->pBuffer == NULL) {
56131 return MA_OUT_OF_MEMORY;
56132 }
56133
56134 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
56135 pRB->ownsBuffer = MA_TRUE;
56136 }
56137
56138 return MA_SUCCESS;
56139 }
56140
56141 MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
56142 {
56143 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
56144 }
56145
56146 MA_API void ma_rb_uninit(ma_rb* pRB)
56147 {
56148 if (pRB == NULL) {
56149 return;
56150 }
56151
56152 if (pRB->ownsBuffer) {
56153 ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);
56154 }
56155 }
56156
56157 MA_API void ma_rb_reset(ma_rb* pRB)
56158 {
56159 if (pRB == NULL) {
56160 return;
56161 }
56162
56163 c89atomic_exchange_32(&pRB->encodedReadOffset, 0);
56164 c89atomic_exchange_32(&pRB->encodedWriteOffset, 0);
56165 }
56166
56167 MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
56168 {
56169 ma_uint32 writeOffset;
56170 ma_uint32 writeOffsetInBytes;
56171 ma_uint32 writeOffsetLoopFlag;
56172 ma_uint32 readOffset;
56173 ma_uint32 readOffsetInBytes;
56174 ma_uint32 readOffsetLoopFlag;
56175 size_t bytesAvailable;
56176 size_t bytesRequested;
56177
56178 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
56179 return MA_INVALID_ARGS;
56180 }
56181
56182 /* The returned buffer should never move ahead of the write pointer. */
56183 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
56184 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56185
56186 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
56187 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56188
56189 /*
56190 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
56191 can only read up to the write pointer. If not, we can only read up to the end of the buffer.
56192 */
56193 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56194 bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
56195 } else {
56196 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
56197 }
56198
56199 bytesRequested = *pSizeInBytes;
56200 if (bytesRequested > bytesAvailable) {
56201 bytesRequested = bytesAvailable;
56202 }
56203
56204 *pSizeInBytes = bytesRequested;
56205 (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
56206
56207 return MA_SUCCESS;
56208 }
56209
56210 MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes)
56211 {
56212 ma_uint32 readOffset;
56213 ma_uint32 readOffsetInBytes;
56214 ma_uint32 readOffsetLoopFlag;
56215 ma_uint32 newReadOffsetInBytes;
56216 ma_uint32 newReadOffsetLoopFlag;
56217
56218 if (pRB == NULL) {
56219 return MA_INVALID_ARGS;
56220 }
56221
56222 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
56223 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56224
56225 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
56226 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
56227 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
56228 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
56229 }
56230
56231 /* Move the read pointer back to the start if necessary. */
56232 newReadOffsetLoopFlag = readOffsetLoopFlag;
56233 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
56234 newReadOffsetInBytes = 0;
56235 newReadOffsetLoopFlag ^= 0x80000000;
56236 }
56237
56238 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
56239
56240 if (ma_rb_pointer_distance(pRB) == 0) {
56241 return MA_AT_END;
56242 } else {
56243 return MA_SUCCESS;
56244 }
56245 }
56246
56247 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
56248 {
56249 ma_uint32 readOffset;
56250 ma_uint32 readOffsetInBytes;
56251 ma_uint32 readOffsetLoopFlag;
56252 ma_uint32 writeOffset;
56253 ma_uint32 writeOffsetInBytes;
56254 ma_uint32 writeOffsetLoopFlag;
56255 size_t bytesAvailable;
56256 size_t bytesRequested;
56257
56258 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
56259 return MA_INVALID_ARGS;
56260 }
56261
56262 /* The returned buffer should never overtake the read buffer. */
56263 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
56264 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56265
56266 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
56267 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56268
56269 /*
56270 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
56271 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
56272 never overtake the read pointer.
56273 */
56274 if (writeOffsetLoopFlag == readOffsetLoopFlag) {
56275 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
56276 } else {
56277 bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
56278 }
56279
56280 bytesRequested = *pSizeInBytes;
56281 if (bytesRequested > bytesAvailable) {
56282 bytesRequested = bytesAvailable;
56283 }
56284
56285 *pSizeInBytes = bytesRequested;
56286 *ppBufferOut = ma_rb__get_write_ptr(pRB);
56287
56288 /* Clear the buffer if desired. */
56289 if (pRB->clearOnWriteAcquire) {
56290 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
56291 }
56292
56293 return MA_SUCCESS;
56294 }
56295
56296 MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes)
56297 {
56298 ma_uint32 writeOffset;
56299 ma_uint32 writeOffsetInBytes;
56300 ma_uint32 writeOffsetLoopFlag;
56301 ma_uint32 newWriteOffsetInBytes;
56302 ma_uint32 newWriteOffsetLoopFlag;
56303
56304 if (pRB == NULL) {
56305 return MA_INVALID_ARGS;
56306 }
56307
56308 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
56309 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56310
56311 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
56312 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
56313 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
56314 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
56315 }
56316
56317 /* Move the read pointer back to the start if necessary. */
56318 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
56319 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
56320 newWriteOffsetInBytes = 0;
56321 newWriteOffsetLoopFlag ^= 0x80000000;
56322 }
56323
56324 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
56325
56326 if (ma_rb_pointer_distance(pRB) == 0) {
56327 return MA_AT_END;
56328 } else {
56329 return MA_SUCCESS;
56330 }
56331 }
56332
56333 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
56334 {
56335 ma_uint32 readOffset;
56336 ma_uint32 readOffsetInBytes;
56337 ma_uint32 readOffsetLoopFlag;
56338 ma_uint32 writeOffset;
56339 ma_uint32 writeOffsetInBytes;
56340 ma_uint32 writeOffsetLoopFlag;
56341 ma_uint32 newReadOffsetInBytes;
56342 ma_uint32 newReadOffsetLoopFlag;
56343
56344 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
56345 return MA_INVALID_ARGS;
56346 }
56347
56348 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
56349 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56350
56351 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
56352 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56353
56354 newReadOffsetLoopFlag = readOffsetLoopFlag;
56355
56356 /* We cannot go past the write buffer. */
56357 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56358 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
56359 newReadOffsetInBytes = writeOffsetInBytes;
56360 } else {
56361 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
56362 }
56363 } else {
56364 /* May end up looping. */
56365 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
56366 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
56367 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
56368 } else {
56369 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
56370 }
56371 }
56372
56373 c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
56374 return MA_SUCCESS;
56375 }
56376
56377 MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
56378 {
56379 ma_uint32 readOffset;
56380 ma_uint32 readOffsetInBytes;
56381 ma_uint32 readOffsetLoopFlag;
56382 ma_uint32 writeOffset;
56383 ma_uint32 writeOffsetInBytes;
56384 ma_uint32 writeOffsetLoopFlag;
56385 ma_uint32 newWriteOffsetInBytes;
56386 ma_uint32 newWriteOffsetLoopFlag;
56387
56388 if (pRB == NULL) {
56389 return MA_INVALID_ARGS;
56390 }
56391
56392 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
56393 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56394
56395 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
56396 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56397
56398 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
56399
56400 /* We cannot go past the write buffer. */
56401 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56402 /* May end up looping. */
56403 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
56404 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
56405 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
56406 } else {
56407 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
56408 }
56409 } else {
56410 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
56411 newWriteOffsetInBytes = readOffsetInBytes;
56412 } else {
56413 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
56414 }
56415 }
56416
56417 c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
56418 return MA_SUCCESS;
56419 }
56420
56421 MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB)
56422 {
56423 ma_uint32 readOffset;
56424 ma_uint32 readOffsetInBytes;
56425 ma_uint32 readOffsetLoopFlag;
56426 ma_uint32 writeOffset;
56427 ma_uint32 writeOffsetInBytes;
56428 ma_uint32 writeOffsetLoopFlag;
56429
56430 if (pRB == NULL) {
56431 return 0;
56432 }
56433
56434 readOffset = c89atomic_load_32(&pRB->encodedReadOffset);
56435 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
56436
56437 writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset);
56438 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
56439
56440 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
56441 return writeOffsetInBytes - readOffsetInBytes;
56442 } else {
56443 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
56444 }
56445 }
56446
56447 MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB)
56448 {
56449 ma_int32 dist;
56450
56451 if (pRB == NULL) {
56452 return 0;
56453 }
56454
56455 dist = ma_rb_pointer_distance(pRB);
56456 if (dist < 0) {
56457 return 0;
56458 }
56459
56460 return dist;
56461 }
56462
56463 MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB)
56464 {
56465 if (pRB == NULL) {
56466 return 0;
56467 }
56468
56469 return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));
56470 }
56471
56472 MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
56473 {
56474 if (pRB == NULL) {
56475 return 0;
56476 }
56477
56478 return pRB->subbufferSizeInBytes;
56479 }
56480
56481 MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
56482 {
56483 if (pRB == NULL) {
56484 return 0;
56485 }
56486
56487 if (pRB->subbufferStrideInBytes == 0) {
56488 return (size_t)pRB->subbufferSizeInBytes;
56489 }
56490
56491 return (size_t)pRB->subbufferStrideInBytes;
56492 }
56493
56494 MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
56495 {
56496 if (pRB == NULL) {
56497 return 0;
56498 }
56499
56500 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
56501 }
56502
56503 MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
56504 {
56505 if (pRB == NULL) {
56506 return NULL;
56507 }
56508
56509 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
56510 }
56511
56512
56513
56514 static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
56515 {
56516 /* 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. */
56517 ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
56518 ma_result result;
56519 ma_uint64 totalFramesRead;
56520
56521 MA_ASSERT(pRB != NULL);
56522
56523 /* We need to run this in a loop since the ring buffer itself may loop. */
56524 totalFramesRead = 0;
56525 while (totalFramesRead < frameCount) {
56526 void* pMappedBuffer;
56527 ma_uint32 mappedFrameCount;
56528 ma_uint64 framesToRead = frameCount - totalFramesRead;
56529 if (framesToRead > 0xFFFFFFFF) {
56530 framesToRead = 0xFFFFFFFF;
56531 }
56532
56533 mappedFrameCount = (ma_uint32)framesToRead;
56534 result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer);
56535 if (result != MA_SUCCESS) {
56536 break;
56537 }
56538
56539 if (mappedFrameCount == 0) {
56540 break; /* <-- End of ring buffer. */
56541 }
56542
56543 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels);
56544
56545 result = ma_pcm_rb_commit_read(pRB, mappedFrameCount);
56546 if (result != MA_SUCCESS) {
56547 break;
56548 }
56549
56550 totalFramesRead += mappedFrameCount;
56551 }
56552
56553 *pFramesRead = totalFramesRead;
56554 return MA_SUCCESS;
56555 }
56556
56557 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)
56558 {
56559 ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
56560 MA_ASSERT(pRB != NULL);
56561
56562 if (pFormat != NULL) {
56563 *pFormat = pRB->format;
56564 }
56565
56566 if (pChannels != NULL) {
56567 *pChannels = pRB->channels;
56568 }
56569
56570 if (pSampleRate != NULL) {
56571 *pSampleRate = pRB->sampleRate;
56572 }
56573
56574 /* Just assume the default channel map. */
56575 if (pChannelMap != NULL) {
56576 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels);
56577 }
56578
56579 return MA_SUCCESS;
56580 }
56581
56582 static ma_data_source_vtable ma_gRBDataSourceVTable =
56583 {
56584 ma_pcm_rb_data_source__on_read,
56585 NULL, /* onSeek */
56586 ma_pcm_rb_data_source__on_get_data_format,
56587 NULL, /* onGetCursor */
56588 NULL, /* onGetLength */
56589 NULL, /* onSetLooping */
56590 0
56591 };
56592
56593 static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
56594 {
56595 MA_ASSERT(pRB != NULL);
56596
56597 return ma_get_bytes_per_frame(pRB->format, pRB->channels);
56598 }
56599
56600 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)
56601 {
56602 ma_uint32 bpf;
56603 ma_result result;
56604
56605 if (pRB == NULL) {
56606 return MA_INVALID_ARGS;
56607 }
56608
56609 MA_ZERO_OBJECT(pRB);
56610
56611 bpf = ma_get_bytes_per_frame(format, channels);
56612 if (bpf == 0) {
56613 return MA_INVALID_ARGS;
56614 }
56615
56616 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
56617 if (result != MA_SUCCESS) {
56618 return result;
56619 }
56620
56621 pRB->format = format;
56622 pRB->channels = channels;
56623 pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */
56624
56625 /* The PCM ring buffer is a data source. We need to get that set up as well. */
56626 {
56627 ma_data_source_config dataSourceConfig = ma_data_source_config_init();
56628 dataSourceConfig.vtable = &ma_gRBDataSourceVTable;
56629
56630 result = ma_data_source_init(&dataSourceConfig, &pRB->ds);
56631 if (result != MA_SUCCESS) {
56632 ma_rb_uninit(&pRB->rb);
56633 return result;
56634 }
56635 }
56636
56637 return MA_SUCCESS;
56638 }
56639
56640 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)
56641 {
56642 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
56643 }
56644
56645 MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
56646 {
56647 if (pRB == NULL) {
56648 return;
56649 }
56650
56651 ma_data_source_uninit(&pRB->ds);
56652 ma_rb_uninit(&pRB->rb);
56653 }
56654
56655 MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)
56656 {
56657 if (pRB == NULL) {
56658 return;
56659 }
56660
56661 ma_rb_reset(&pRB->rb);
56662 }
56663
56664 MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
56665 {
56666 size_t sizeInBytes;
56667 ma_result result;
56668
56669 if (pRB == NULL || pSizeInFrames == NULL) {
56670 return MA_INVALID_ARGS;
56671 }
56672
56673 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
56674
56675 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
56676 if (result != MA_SUCCESS) {
56677 return result;
56678 }
56679
56680 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
56681 return MA_SUCCESS;
56682 }
56683
56684 MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
56685 {
56686 if (pRB == NULL) {
56687 return MA_INVALID_ARGS;
56688 }
56689
56690 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
56691 }
56692
56693 MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
56694 {
56695 size_t sizeInBytes;
56696 ma_result result;
56697
56698 if (pRB == NULL) {
56699 return MA_INVALID_ARGS;
56700 }
56701
56702 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
56703
56704 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
56705 if (result != MA_SUCCESS) {
56706 return result;
56707 }
56708
56709 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
56710 return MA_SUCCESS;
56711 }
56712
56713 MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
56714 {
56715 if (pRB == NULL) {
56716 return MA_INVALID_ARGS;
56717 }
56718
56719 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
56720 }
56721
56722 MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
56723 {
56724 if (pRB == NULL) {
56725 return MA_INVALID_ARGS;
56726 }
56727
56728 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
56729 }
56730
56731 MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
56732 {
56733 if (pRB == NULL) {
56734 return MA_INVALID_ARGS;
56735 }
56736
56737 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
56738 }
56739
56740 MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB)
56741 {
56742 if (pRB == NULL) {
56743 return 0;
56744 }
56745
56746 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
56747 }
56748
56749 MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)
56750 {
56751 if (pRB == NULL) {
56752 return 0;
56753 }
56754
56755 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
56756 }
56757
56758 MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)
56759 {
56760 if (pRB == NULL) {
56761 return 0;
56762 }
56763
56764 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
56765 }
56766
56767 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)
56768 {
56769 if (pRB == NULL) {
56770 return 0;
56771 }
56772
56773 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
56774 }
56775
56776 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)
56777 {
56778 if (pRB == NULL) {
56779 return 0;
56780 }
56781
56782 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
56783 }
56784
56785 MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
56786 {
56787 if (pRB == NULL) {
56788 return 0;
56789 }
56790
56791 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
56792 }
56793
56794 MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
56795 {
56796 if (pRB == NULL) {
56797 return NULL;
56798 }
56799
56800 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
56801 }
56802
56803 MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB)
56804 {
56805 if (pRB == NULL) {
56806 return ma_format_unknown;
56807 }
56808
56809 return pRB->format;
56810 }
56811
56812 MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB)
56813 {
56814 if (pRB == NULL) {
56815 return 0;
56816 }
56817
56818 return pRB->channels;
56819 }
56820
56821 MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB)
56822 {
56823 if (pRB == NULL) {
56824 return 0;
56825 }
56826
56827 return pRB->sampleRate;
56828 }
56829
56830 MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate)
56831 {
56832 if (pRB == NULL) {
56833 return;
56834 }
56835
56836 pRB->sampleRate = sampleRate;
56837 }
56838
56839
56840
56841 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)
56842 {
56843 ma_result result;
56844 ma_uint32 sizeInFrames;
56845
56846 sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
56847 if (sizeInFrames == 0) {
56848 return MA_INVALID_ARGS;
56849 }
56850
56851 result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
56852 if (result != MA_SUCCESS) {
56853 return result;
56854 }
56855
56856 /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
56857 ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
56858
56859 return MA_SUCCESS;
56860 }
56861
56862 MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
56863 {
56864 ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
56865 return MA_SUCCESS;
56866 }
56867
56868
56869
56870 /**************************************************************************************************************************************************************
56871
56872 Miscellaneous Helpers
56873
56874 **************************************************************************************************************************************************************/
56875 MA_API const char* ma_result_description(ma_result result)
56876 {
56877 switch (result)
56878 {
56879 case MA_SUCCESS: return "No error";
56880 case MA_ERROR: return "Unknown error";
56881 case MA_INVALID_ARGS: return "Invalid argument";
56882 case MA_INVALID_OPERATION: return "Invalid operation";
56883 case MA_OUT_OF_MEMORY: return "Out of memory";
56884 case MA_OUT_OF_RANGE: return "Out of range";
56885 case MA_ACCESS_DENIED: return "Permission denied";
56886 case MA_DOES_NOT_EXIST: return "Resource does not exist";
56887 case MA_ALREADY_EXISTS: return "Resource already exists";
56888 case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
56889 case MA_INVALID_FILE: return "Invalid file";
56890 case MA_TOO_BIG: return "Too large";
56891 case MA_PATH_TOO_LONG: return "Path too long";
56892 case MA_NAME_TOO_LONG: return "Name too long";
56893 case MA_NOT_DIRECTORY: return "Not a directory";
56894 case MA_IS_DIRECTORY: return "Is a directory";
56895 case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
56896 case MA_AT_END: return "At end";
56897 case MA_NO_SPACE: return "No space available";
56898 case MA_BUSY: return "Device or resource busy";
56899 case MA_IO_ERROR: return "Input/output error";
56900 case MA_INTERRUPT: return "Interrupted";
56901 case MA_UNAVAILABLE: return "Resource unavailable";
56902 case MA_ALREADY_IN_USE: return "Resource already in use";
56903 case MA_BAD_ADDRESS: return "Bad address";
56904 case MA_BAD_SEEK: return "Illegal seek";
56905 case MA_BAD_PIPE: return "Broken pipe";
56906 case MA_DEADLOCK: return "Deadlock";
56907 case MA_TOO_MANY_LINKS: return "Too many links";
56908 case MA_NOT_IMPLEMENTED: return "Not implemented";
56909 case MA_NO_MESSAGE: return "No message of desired type";
56910 case MA_BAD_MESSAGE: return "Invalid message";
56911 case MA_NO_DATA_AVAILABLE: return "No data available";
56912 case MA_INVALID_DATA: return "Invalid data";
56913 case MA_TIMEOUT: return "Timeout";
56914 case MA_NO_NETWORK: return "Network unavailable";
56915 case MA_NOT_UNIQUE: return "Not unique";
56916 case MA_NOT_SOCKET: return "Socket operation on non-socket";
56917 case MA_NO_ADDRESS: return "Destination address required";
56918 case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
56919 case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
56920 case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
56921 case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
56922 case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
56923 case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
56924 case MA_CONNECTION_RESET: return "Connection reset";
56925 case MA_ALREADY_CONNECTED: return "Already connected";
56926 case MA_NOT_CONNECTED: return "Not connected";
56927 case MA_CONNECTION_REFUSED: return "Connection refused";
56928 case MA_NO_HOST: return "No host";
56929 case MA_IN_PROGRESS: return "Operation in progress";
56930 case MA_CANCELLED: return "Operation cancelled";
56931 case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
56932
56933 case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
56934 case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
56935 case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
56936 case MA_NO_BACKEND: return "No backend";
56937 case MA_NO_DEVICE: return "No device";
56938 case MA_API_NOT_FOUND: return "API not found";
56939 case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
56940
56941 case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
56942 case MA_DEVICE_NOT_STARTED: return "Device not started";
56943
56944 case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
56945 case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
56946 case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
56947 case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
56948
56949 default: return "Unknown error";
56950 }
56951 }
56952
56953 MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
56954 {
56955 if (pAllocationCallbacks != NULL) {
56956 if (pAllocationCallbacks->onMalloc != NULL) {
56957 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
56958 } else {
56959 return NULL; /* Do not fall back to the default implementation. */
56960 }
56961 } else {
56962 return ma__malloc_default(sz, NULL);
56963 }
56964 }
56965
56966 MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
56967 {
56968 void* p = ma_malloc(sz, pAllocationCallbacks);
56969 if (p != NULL) {
56970 MA_ZERO_MEMORY(p, sz);
56971 }
56972
56973 return p;
56974 }
56975
56976 MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
56977 {
56978 if (pAllocationCallbacks != NULL) {
56979 if (pAllocationCallbacks->onRealloc != NULL) {
56980 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
56981 } else {
56982 return NULL; /* Do not fall back to the default implementation. */
56983 }
56984 } else {
56985 return ma__realloc_default(p, sz, NULL);
56986 }
56987 }
56988
56989 MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
56990 {
56991 if (p == NULL) {
56992 return;
56993 }
56994
56995 if (pAllocationCallbacks != NULL) {
56996 if (pAllocationCallbacks->onFree != NULL) {
56997 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
56998 } else {
56999 return; /* Do no fall back to the default implementation. */
57000 }
57001 } else {
57002 ma__free_default(p, NULL);
57003 }
57004 }
57005
57006 MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
57007 {
57008 size_t extraBytes;
57009 void* pUnaligned;
57010 void* pAligned;
57011
57012 if (alignment == 0) {
57013 return 0;
57014 }
57015
57016 extraBytes = alignment-1 + sizeof(void*);
57017
57018 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
57019 if (pUnaligned == NULL) {
57020 return NULL;
57021 }
57022
57023 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
57024 ((void**)pAligned)[-1] = pUnaligned;
57025
57026 return pAligned;
57027 }
57028
57029 MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
57030 {
57031 ma_free(((void**)p)[-1], pAllocationCallbacks);
57032 }
57033
57034 MA_API const char* ma_get_format_name(ma_format format)
57035 {
57036 switch (format)
57037 {
57038 case ma_format_unknown: return "Unknown";
57039 case ma_format_u8: return "8-bit Unsigned Integer";
57040 case ma_format_s16: return "16-bit Signed Integer";
57041 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
57042 case ma_format_s32: return "32-bit Signed Integer";
57043 case ma_format_f32: return "32-bit IEEE Floating Point";
57044 default: return "Invalid";
57045 }
57046 }
57047
57048 MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
57049 {
57050 ma_uint32 i;
57051 for (i = 0; i < channels; ++i) {
57052 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
57053 }
57054 }
57055
57056
57057 MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
57058 {
57059 ma_uint32 sizes[] = {
57060 0, /* unknown */
57061 1, /* u8 */
57062 2, /* s16 */
57063 3, /* s24 */
57064 4, /* s32 */
57065 4, /* f32 */
57066 };
57067 return sizes[format];
57068 }
57069
57070
57071
57072 #define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0
57073 #define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0)
57074 #define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0
57075 #define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0)
57076
57077 MA_API ma_data_source_config ma_data_source_config_init(void)
57078 {
57079 ma_data_source_config config;
57080
57081 MA_ZERO_OBJECT(&config);
57082
57083 return config;
57084 }
57085
57086
57087 MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource)
57088 {
57089 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57090
57091 if (pDataSource == NULL) {
57092 return MA_INVALID_ARGS;
57093 }
57094
57095 MA_ZERO_OBJECT(pDataSourceBase);
57096
57097 if (pConfig == NULL) {
57098 return MA_INVALID_ARGS;
57099 }
57100
57101 pDataSourceBase->vtable = pConfig->vtable;
57102 pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
57103 pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
57104 pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
57105 pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
57106 pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */
57107 pDataSourceBase->pNext = NULL;
57108 pDataSourceBase->onGetNext = NULL;
57109
57110 return MA_SUCCESS;
57111 }
57112
57113 MA_API void ma_data_source_uninit(ma_data_source* pDataSource)
57114 {
57115 if (pDataSource == NULL) {
57116 return;
57117 }
57118
57119 /*
57120 This is placeholder in case we need this later. Data sources need to call this in their
57121 uninitialization routine to ensure things work later on if something is added here.
57122 */
57123 }
57124
57125 static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource)
57126 {
57127 ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource;
57128
57129 MA_ASSERT(pDataSource != NULL);
57130 MA_ASSERT(ppCurrentDataSource != NULL);
57131
57132 if (pCurrentDataSource->pCurrent == NULL) {
57133 /*
57134 The current data source is NULL. If we're using this in the context of a chain we need to return NULL
57135 here so that we don't end up looping. Otherwise we just return the data source itself.
57136 */
57137 if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) {
57138 pCurrentDataSource = NULL;
57139 } else {
57140 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. */
57141 }
57142 } else {
57143 pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent;
57144 }
57145
57146 *ppCurrentDataSource = pCurrentDataSource;
57147
57148 return MA_SUCCESS;
57149 }
57150
57151 static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
57152 {
57153 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57154 ma_result result;
57155 ma_uint64 framesRead = 0;
57156 ma_bool32 loop = ma_data_source_is_looping(pDataSource);
57157
57158 if (pDataSourceBase == NULL) {
57159 return MA_AT_END;
57160 }
57161
57162 if (frameCount == 0) {
57163 return MA_INVALID_ARGS;
57164 }
57165
57166 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))) {
57167 /* 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. */
57168 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
57169 } else {
57170 /* Need to clamp to within the range. */
57171 ma_uint64 relativeCursor;
57172 ma_uint64 absoluteCursor;
57173
57174 result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor);
57175 if (result != MA_SUCCESS) {
57176 /* 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. */
57177 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
57178 } else {
57179 ma_uint64 rangeBeg;
57180 ma_uint64 rangeEnd;
57181
57182 /* We have the cursor. We need to make sure we don't read beyond our range. */
57183 rangeBeg = pDataSourceBase->rangeBegInFrames;
57184 rangeEnd = pDataSourceBase->rangeEndInFrames;
57185
57186 absoluteCursor = rangeBeg + relativeCursor;
57187
57188 /* If looping, make sure we're within range. */
57189 if (loop) {
57190 if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
57191 rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);
57192 }
57193 }
57194
57195 if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) {
57196 frameCount = (rangeEnd - absoluteCursor);
57197 }
57198
57199 /*
57200 If the cursor is sitting on the end of the range the frame count will be set to 0 which can
57201 result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return
57202 MA_AT_END so the higher level function can know about it.
57203 */
57204 if (frameCount > 0) {
57205 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
57206 } else {
57207 result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */
57208 }
57209 }
57210 }
57211
57212 if (pFramesRead != NULL) {
57213 *pFramesRead = framesRead;
57214 }
57215
57216 /* We need to make sure MA_AT_END is returned if we hit the end of the range. */
57217 if (result == MA_SUCCESS && framesRead == 0) {
57218 result = MA_AT_END;
57219 }
57220
57221 return result;
57222 }
57223
57224 MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
57225 {
57226 ma_result result = MA_SUCCESS;
57227 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57228 ma_data_source_base* pCurrentDataSource;
57229 void* pRunningFramesOut = pFramesOut;
57230 ma_uint64 totalFramesProcessed = 0;
57231 ma_format format;
57232 ma_uint32 channels;
57233 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. */
57234 ma_bool32 loop;
57235
57236 if (pFramesRead != NULL) {
57237 *pFramesRead = 0;
57238 }
57239
57240 if (frameCount == 0) {
57241 return MA_INVALID_ARGS;
57242 }
57243
57244 if (pDataSourceBase == NULL) {
57245 return MA_INVALID_ARGS;
57246 }
57247
57248 loop = ma_data_source_is_looping(pDataSource);
57249
57250 /*
57251 We need to know the data format so we can advance the output buffer as we read frames. If this
57252 fails, chaining will not work and we'll just read as much as we can from the current source.
57253 */
57254 if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) {
57255 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
57256 if (result != MA_SUCCESS) {
57257 return result;
57258 }
57259
57260 return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead);
57261 }
57262
57263 /*
57264 Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and
57265 only the current data source will be read from.
57266 */
57267
57268 /* Keep reading until we've read as many frames as possible. */
57269 while (totalFramesProcessed < frameCount) {
57270 ma_uint64 framesProcessed;
57271 ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
57272
57273 /* We need to resolve the data source that we'll actually be reading from. */
57274 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
57275 if (result != MA_SUCCESS) {
57276 break;
57277 }
57278
57279 if (pCurrentDataSource == NULL) {
57280 break;
57281 }
57282
57283 result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
57284 totalFramesProcessed += framesProcessed;
57285
57286 /*
57287 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
57288 not necessarily considered an error.
57289 */
57290 if (result != MA_SUCCESS && result != MA_AT_END) {
57291 break;
57292 }
57293
57294 /*
57295 We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned
57296 MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame.
57297 */
57298 if (result == MA_AT_END) {
57299 /*
57300 The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't
57301 accidentally return MA_AT_END when data has been read in prior loop iterations. at the
57302 end of this function, the result will be checked for MA_SUCCESS, and if the total
57303 number of frames processed is 0, will be explicitly set to MA_AT_END.
57304 */
57305 result = MA_SUCCESS;
57306
57307 /*
57308 We reached the end. If we're looping, we just loop back to the start of the current
57309 data source. If we're not looping we need to check if we have another in the chain, and
57310 if so, switch to it.
57311 */
57312 if (loop) {
57313 if (framesProcessed == 0) {
57314 emptyLoopCounter += 1;
57315 if (emptyLoopCounter > 1) {
57316 break; /* Infinite loop detected. Get out. */
57317 }
57318 } else {
57319 emptyLoopCounter = 0;
57320 }
57321
57322 result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames);
57323 if (result != MA_SUCCESS) {
57324 break; /* Failed to loop. Abort. */
57325 }
57326
57327 /* Don't return MA_AT_END for looping sounds. */
57328 result = MA_SUCCESS;
57329 } else {
57330 if (pCurrentDataSource->pNext != NULL) {
57331 pDataSourceBase->pCurrent = pCurrentDataSource->pNext;
57332 } else if (pCurrentDataSource->onGetNext != NULL) {
57333 pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource);
57334 if (pDataSourceBase->pCurrent == NULL) {
57335 break; /* Our callback did not return a next data source. We're done. */
57336 }
57337 } else {
57338 /* Reached the end of the chain. We're done. */
57339 break;
57340 }
57341
57342 /* The next data source needs to be rewound to ensure data is read in looping scenarios. */
57343 result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0);
57344 if (result != MA_SUCCESS) {
57345 break;
57346 }
57347 }
57348 }
57349
57350 if (pRunningFramesOut != NULL) {
57351 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
57352 }
57353 }
57354
57355 if (pFramesRead != NULL) {
57356 *pFramesRead = totalFramesProcessed;
57357 }
57358
57359 MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */
57360
57361 if (result == MA_SUCCESS && totalFramesProcessed == 0) {
57362 result = MA_AT_END;
57363 }
57364
57365 return result;
57366 }
57367
57368 MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked)
57369 {
57370 return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked);
57371 }
57372
57373 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
57374 {
57375 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57376
57377 if (pDataSourceBase == NULL) {
57378 return MA_SUCCESS;
57379 }
57380
57381 if (pDataSourceBase->vtable->onSeek == NULL) {
57382 return MA_NOT_IMPLEMENTED;
57383 }
57384
57385 if (frameIndex > pDataSourceBase->rangeEndInFrames) {
57386 return MA_INVALID_OPERATION; /* Trying to seek to far forward. */
57387 }
57388
57389 return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);
57390 }
57391
57392 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)
57393 {
57394 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57395 ma_result result;
57396 ma_format format;
57397 ma_uint32 channels;
57398 ma_uint32 sampleRate;
57399
57400 /* Initialize to defaults for safety just in case the data source does not implement this callback. */
57401 if (pFormat != NULL) {
57402 *pFormat = ma_format_unknown;
57403 }
57404 if (pChannels != NULL) {
57405 *pChannels = 0;
57406 }
57407 if (pSampleRate != NULL) {
57408 *pSampleRate = 0;
57409 }
57410 if (pChannelMap != NULL) {
57411 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
57412 }
57413
57414 if (pDataSourceBase == NULL) {
57415 return MA_INVALID_ARGS;
57416 }
57417
57418 if (pDataSourceBase->vtable->onGetDataFormat == NULL) {
57419 return MA_NOT_IMPLEMENTED;
57420 }
57421
57422 result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap);
57423 if (result != MA_SUCCESS) {
57424 return result;
57425 }
57426
57427 if (pFormat != NULL) {
57428 *pFormat = format;
57429 }
57430 if (pChannels != NULL) {
57431 *pChannels = channels;
57432 }
57433 if (pSampleRate != NULL) {
57434 *pSampleRate = sampleRate;
57435 }
57436
57437 /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */
57438
57439 return MA_SUCCESS;
57440 }
57441
57442 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
57443 {
57444 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57445 ma_result result;
57446 ma_uint64 cursor;
57447
57448 if (pCursor == NULL) {
57449 return MA_INVALID_ARGS;
57450 }
57451
57452 *pCursor = 0;
57453
57454 if (pDataSourceBase == NULL) {
57455 return MA_SUCCESS;
57456 }
57457
57458 if (pDataSourceBase->vtable->onGetCursor == NULL) {
57459 return MA_NOT_IMPLEMENTED;
57460 }
57461
57462 result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor);
57463 if (result != MA_SUCCESS) {
57464 return result;
57465 }
57466
57467 /* The cursor needs to be made relative to the start of the range. */
57468 if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */
57469 *pCursor = 0;
57470 } else {
57471 *pCursor = cursor - pDataSourceBase->rangeBegInFrames;
57472 }
57473
57474 return MA_SUCCESS;
57475 }
57476
57477 MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
57478 {
57479 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57480
57481 if (pLength == NULL) {
57482 return MA_INVALID_ARGS;
57483 }
57484
57485 *pLength = 0;
57486
57487 if (pDataSourceBase == NULL) {
57488 return MA_INVALID_ARGS;
57489 }
57490
57491 /*
57492 If we have a range defined we'll use that to determine the length. This is one of rare times
57493 where we'll actually trust the caller. If they've set the range, I think it's mostly safe to
57494 assume they've set it based on some higher level knowledge of the structure of the sound bank.
57495 */
57496 if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) {
57497 *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames;
57498 return MA_SUCCESS;
57499 }
57500
57501 /*
57502 Getting here means a range is not defined so we'll need to get the data source itself to tell
57503 us the length.
57504 */
57505 if (pDataSourceBase->vtable->onGetLength == NULL) {
57506 return MA_NOT_IMPLEMENTED;
57507 }
57508
57509 return pDataSourceBase->vtable->onGetLength(pDataSource, pLength);
57510 }
57511
57512 MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor)
57513 {
57514 ma_result result;
57515 ma_uint64 cursorInPCMFrames;
57516 ma_uint32 sampleRate;
57517
57518 if (pCursor == NULL) {
57519 return MA_INVALID_ARGS;
57520 }
57521
57522 *pCursor = 0;
57523
57524 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames);
57525 if (result != MA_SUCCESS) {
57526 return result;
57527 }
57528
57529 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
57530 if (result != MA_SUCCESS) {
57531 return result;
57532 }
57533
57534 /* 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. */
57535 *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;
57536
57537 return MA_SUCCESS;
57538 }
57539
57540 MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength)
57541 {
57542 ma_result result;
57543 ma_uint64 lengthInPCMFrames;
57544 ma_uint32 sampleRate;
57545
57546 if (pLength == NULL) {
57547 return MA_INVALID_ARGS;
57548 }
57549
57550 *pLength = 0;
57551
57552 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames);
57553 if (result != MA_SUCCESS) {
57554 return result;
57555 }
57556
57557 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
57558 if (result != MA_SUCCESS) {
57559 return result;
57560 }
57561
57562 /* 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. */
57563 *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate;
57564
57565 return MA_SUCCESS;
57566 }
57567
57568 MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
57569 {
57570 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57571
57572 if (pDataSource == NULL) {
57573 return MA_INVALID_ARGS;
57574 }
57575
57576 c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);
57577
57578 /* If there's no callback for this just treat it as a successful no-op. */
57579 if (pDataSourceBase->vtable->onSetLooping == NULL) {
57580 return MA_SUCCESS;
57581 }
57582
57583 return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);
57584 }
57585
57586 MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource)
57587 {
57588 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57589
57590 if (pDataSource == NULL) {
57591 return MA_FALSE;
57592 }
57593
57594 return c89atomic_load_32(&pDataSourceBase->isLooping);
57595 }
57596
57597 MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
57598 {
57599 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57600 ma_result result;
57601 ma_uint64 relativeCursor;
57602 ma_uint64 absoluteCursor;
57603 ma_bool32 doSeekAdjustment = MA_FALSE;
57604
57605 if (pDataSource == NULL) {
57606 return MA_INVALID_ARGS;
57607 }
57608
57609 if (rangeEndInFrames < rangeBegInFrames) {
57610 return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */
57611 }
57612
57613 /*
57614 We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now
57615 so we can calculate it's absolute position before we change the range.
57616 */
57617 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor);
57618 if (result == MA_SUCCESS) {
57619 doSeekAdjustment = MA_TRUE;
57620 absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames;
57621 } else {
57622 /*
57623 We couldn't get the position of the cursor. It probably means the data source has no notion
57624 of a cursor. We'll just leave it at position 0. Don't treat this as an error.
57625 */
57626 doSeekAdjustment = MA_FALSE;
57627 relativeCursor = 0;
57628 absoluteCursor = 0;
57629 }
57630
57631 pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
57632 pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
57633
57634 /*
57635 The commented out logic below was intended to maintain loop points in response to a change in the
57636 range. However, this is not useful because it results in the sound breaking when you move the range
57637 outside of the old loop points. I'm simplifying this by simply resetting the loop points. The
57638 caller is expected to update their loop points if they change the range.
57639
57640 In practice this should be mostly a non-issue because the majority of the time the range will be
57641 set once right after initialization.
57642 */
57643 pDataSourceBase->loopBegInFrames = 0;
57644 pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);
57645
57646
57647 /*
57648 Seek to within range. Note that our seek positions here are relative to the new range. We don't want
57649 do do this if we failed to retrieve the cursor earlier on because it probably means the data source
57650 has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but
57651 I'm just not even going to attempt it.
57652 */
57653 if (doSeekAdjustment) {
57654 if (absoluteCursor < rangeBegInFrames) {
57655 ma_data_source_seek_to_pcm_frame(pDataSource, 0);
57656 } else if (absoluteCursor > rangeEndInFrames) {
57657 ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
57658 }
57659 }
57660
57661 return MA_SUCCESS;
57662 }
57663
57664 MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
57665 {
57666 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57667
57668 if (pDataSource == NULL) {
57669 return;
57670 }
57671
57672 if (pRangeBegInFrames != NULL) {
57673 *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
57674 }
57675
57676 if (pRangeEndInFrames != NULL) {
57677 *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;
57678 }
57679 }
57680
57681 MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)
57682 {
57683 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57684
57685 if (pDataSource == NULL) {
57686 return MA_INVALID_ARGS;
57687 }
57688
57689 if (loopEndInFrames < loopBegInFrames) {
57690 return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */
57691 }
57692
57693 if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {
57694 return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */
57695 }
57696
57697 pDataSourceBase->loopBegInFrames = loopBegInFrames;
57698 pDataSourceBase->loopEndInFrames = loopEndInFrames;
57699
57700 /* The end cannot exceed the range. */
57701 if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
57702 pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);
57703 }
57704
57705 return MA_SUCCESS;
57706 }
57707
57708 MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
57709 {
57710 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57711
57712 if (pDataSource == NULL) {
57713 return;
57714 }
57715
57716 if (pLoopBegInFrames != NULL) {
57717 *pLoopBegInFrames = pDataSourceBase->loopBegInFrames;
57718 }
57719
57720 if (pLoopEndInFrames != NULL) {
57721 *pLoopEndInFrames = pDataSourceBase->loopEndInFrames;
57722 }
57723 }
57724
57725 MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource)
57726 {
57727 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57728
57729 if (pDataSource == NULL) {
57730 return MA_INVALID_ARGS;
57731 }
57732
57733 pDataSourceBase->pCurrent = pCurrentDataSource;
57734
57735 return MA_SUCCESS;
57736 }
57737
57738 MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource)
57739 {
57740 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57741
57742 if (pDataSource == NULL) {
57743 return NULL;
57744 }
57745
57746 return pDataSourceBase->pCurrent;
57747 }
57748
57749 MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource)
57750 {
57751 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57752
57753 if (pDataSource == NULL) {
57754 return MA_INVALID_ARGS;
57755 }
57756
57757 pDataSourceBase->pNext = pNextDataSource;
57758
57759 return MA_SUCCESS;
57760 }
57761
57762 MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource)
57763 {
57764 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57765
57766 if (pDataSource == NULL) {
57767 return NULL;
57768 }
57769
57770 return pDataSourceBase->pNext;
57771 }
57772
57773 MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext)
57774 {
57775 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
57776
57777 if (pDataSource == NULL) {
57778 return MA_INVALID_ARGS;
57779 }
57780
57781 pDataSourceBase->onGetNext = onGetNext;
57782
57783 return MA_SUCCESS;
57784 }
57785
57786 MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource)
57787 {
57788 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
57789
57790 if (pDataSource == NULL) {
57791 return NULL;
57792 }
57793
57794 return pDataSourceBase->onGetNext;
57795 }
57796
57797
57798 static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
57799 {
57800 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57801 ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE);
57802
57803 if (pFramesRead != NULL) {
57804 *pFramesRead = framesRead;
57805 }
57806
57807 if (framesRead < frameCount || framesRead == 0) {
57808 return MA_AT_END;
57809 }
57810
57811 return MA_SUCCESS;
57812 }
57813
57814 static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
57815 {
57816 return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);
57817 }
57818
57819 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)
57820 {
57821 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57822
57823 *pFormat = pAudioBufferRef->format;
57824 *pChannels = pAudioBufferRef->channels;
57825 *pSampleRate = pAudioBufferRef->sampleRate;
57826 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels);
57827
57828 return MA_SUCCESS;
57829 }
57830
57831 static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
57832 {
57833 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57834
57835 *pCursor = pAudioBufferRef->cursor;
57836
57837 return MA_SUCCESS;
57838 }
57839
57840 static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
57841 {
57842 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
57843
57844 *pLength = pAudioBufferRef->sizeInFrames;
57845
57846 return MA_SUCCESS;
57847 }
57848
57849 static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable =
57850 {
57851 ma_audio_buffer_ref__data_source_on_read,
57852 ma_audio_buffer_ref__data_source_on_seek,
57853 ma_audio_buffer_ref__data_source_on_get_data_format,
57854 ma_audio_buffer_ref__data_source_on_get_cursor,
57855 ma_audio_buffer_ref__data_source_on_get_length,
57856 NULL, /* onSetLooping */
57857 0
57858 };
57859
57860 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)
57861 {
57862 ma_result result;
57863 ma_data_source_config dataSourceConfig;
57864
57865 if (pAudioBufferRef == NULL) {
57866 return MA_INVALID_ARGS;
57867 }
57868
57869 MA_ZERO_OBJECT(pAudioBufferRef);
57870
57871 dataSourceConfig = ma_data_source_config_init();
57872 dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable;
57873
57874 result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds);
57875 if (result != MA_SUCCESS) {
57876 return result;
57877 }
57878
57879 pAudioBufferRef->format = format;
57880 pAudioBufferRef->channels = channels;
57881 pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */
57882 pAudioBufferRef->cursor = 0;
57883 pAudioBufferRef->sizeInFrames = sizeInFrames;
57884 pAudioBufferRef->pData = pData;
57885
57886 return MA_SUCCESS;
57887 }
57888
57889 MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef)
57890 {
57891 if (pAudioBufferRef == NULL) {
57892 return;
57893 }
57894
57895 ma_data_source_uninit(&pAudioBufferRef->ds);
57896 }
57897
57898 MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)
57899 {
57900 if (pAudioBufferRef == NULL) {
57901 return MA_INVALID_ARGS;
57902 }
57903
57904 pAudioBufferRef->cursor = 0;
57905 pAudioBufferRef->sizeInFrames = sizeInFrames;
57906 pAudioBufferRef->pData = pData;
57907
57908 return MA_SUCCESS;
57909 }
57910
57911 MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
57912 {
57913 ma_uint64 totalFramesRead = 0;
57914
57915 if (pAudioBufferRef == NULL) {
57916 return 0;
57917 }
57918
57919 if (frameCount == 0) {
57920 return 0;
57921 }
57922
57923 while (totalFramesRead < frameCount) {
57924 ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
57925 ma_uint64 framesRemaining = frameCount - totalFramesRead;
57926 ma_uint64 framesToRead;
57927
57928 framesToRead = framesRemaining;
57929 if (framesToRead > framesAvailable) {
57930 framesToRead = framesAvailable;
57931 }
57932
57933 if (pFramesOut != NULL) {
57934 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);
57935 }
57936
57937 totalFramesRead += framesToRead;
57938
57939 pAudioBufferRef->cursor += framesToRead;
57940 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
57941 if (loop) {
57942 pAudioBufferRef->cursor = 0;
57943 } else {
57944 break; /* We've reached the end and we're not looping. Done. */
57945 }
57946 }
57947
57948 MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);
57949 }
57950
57951 return totalFramesRead;
57952 }
57953
57954 MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex)
57955 {
57956 if (pAudioBufferRef == NULL) {
57957 return MA_INVALID_ARGS;
57958 }
57959
57960 if (frameIndex > pAudioBufferRef->sizeInFrames) {
57961 return MA_INVALID_ARGS;
57962 }
57963
57964 pAudioBufferRef->cursor = (size_t)frameIndex;
57965
57966 return MA_SUCCESS;
57967 }
57968
57969 MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)
57970 {
57971 ma_uint64 framesAvailable;
57972 ma_uint64 frameCount = 0;
57973
57974 if (ppFramesOut != NULL) {
57975 *ppFramesOut = NULL; /* Safety. */
57976 }
57977
57978 if (pFrameCount != NULL) {
57979 frameCount = *pFrameCount;
57980 *pFrameCount = 0; /* Safety. */
57981 }
57982
57983 if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
57984 return MA_INVALID_ARGS;
57985 }
57986
57987 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
57988 if (frameCount > framesAvailable) {
57989 frameCount = framesAvailable;
57990 }
57991
57992 *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));
57993 *pFrameCount = frameCount;
57994
57995 return MA_SUCCESS;
57996 }
57997
57998 MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount)
57999 {
58000 ma_uint64 framesAvailable;
58001
58002 if (pAudioBufferRef == NULL) {
58003 return MA_INVALID_ARGS;
58004 }
58005
58006 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
58007 if (frameCount > framesAvailable) {
58008 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. */
58009 }
58010
58011 pAudioBufferRef->cursor += frameCount;
58012
58013 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
58014 return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */
58015 } else {
58016 return MA_SUCCESS;
58017 }
58018 }
58019
58020 MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef)
58021 {
58022 if (pAudioBufferRef == NULL) {
58023 return MA_FALSE;
58024 }
58025
58026 return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;
58027 }
58028
58029 MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor)
58030 {
58031 if (pCursor == NULL) {
58032 return MA_INVALID_ARGS;
58033 }
58034
58035 *pCursor = 0;
58036
58037 if (pAudioBufferRef == NULL) {
58038 return MA_INVALID_ARGS;
58039 }
58040
58041 *pCursor = pAudioBufferRef->cursor;
58042
58043 return MA_SUCCESS;
58044 }
58045
58046 MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength)
58047 {
58048 if (pLength == NULL) {
58049 return MA_INVALID_ARGS;
58050 }
58051
58052 *pLength = 0;
58053
58054 if (pAudioBufferRef == NULL) {
58055 return MA_INVALID_ARGS;
58056 }
58057
58058 *pLength = pAudioBufferRef->sizeInFrames;
58059
58060 return MA_SUCCESS;
58061 }
58062
58063 MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames)
58064 {
58065 if (pAvailableFrames == NULL) {
58066 return MA_INVALID_ARGS;
58067 }
58068
58069 *pAvailableFrames = 0;
58070
58071 if (pAudioBufferRef == NULL) {
58072 return MA_INVALID_ARGS;
58073 }
58074
58075 if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {
58076 *pAvailableFrames = 0;
58077 } else {
58078 *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
58079 }
58080
58081 return MA_SUCCESS;
58082 }
58083
58084
58085
58086
58087 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)
58088 {
58089 ma_audio_buffer_config config;
58090
58091 MA_ZERO_OBJECT(&config);
58092 config.format = format;
58093 config.channels = channels;
58094 config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */
58095 config.sizeInFrames = sizeInFrames;
58096 config.pData = pData;
58097 ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
58098
58099 return config;
58100 }
58101
58102 static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
58103 {
58104 ma_result result;
58105
58106 if (pAudioBuffer == NULL) {
58107 return MA_INVALID_ARGS;
58108 }
58109
58110 MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */
58111
58112 if (pConfig == NULL) {
58113 return MA_INVALID_ARGS;
58114 }
58115
58116 if (pConfig->sizeInFrames == 0) {
58117 return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */
58118 }
58119
58120 result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);
58121 if (result != MA_SUCCESS) {
58122 return result;
58123 }
58124
58125 /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */
58126 pAudioBuffer->ref.sampleRate = pConfig->sampleRate;
58127
58128 ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
58129
58130 if (doCopy) {
58131 ma_uint64 allocationSizeInBytes;
58132 void* pData;
58133
58134 allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);
58135 if (allocationSizeInBytes > MA_SIZE_MAX) {
58136 return MA_OUT_OF_MEMORY; /* Too big. */
58137 }
58138
58139 pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */
58140 if (pData == NULL) {
58141 return MA_OUT_OF_MEMORY;
58142 }
58143
58144 if (pConfig->pData != NULL) {
58145 ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58146 } else {
58147 ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58148 }
58149
58150 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);
58151 pAudioBuffer->ownsData = MA_TRUE;
58152 } else {
58153 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);
58154 pAudioBuffer->ownsData = MA_FALSE;
58155 }
58156
58157 return MA_SUCCESS;
58158 }
58159
58160 static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)
58161 {
58162 if (pAudioBuffer == NULL) {
58163 return;
58164 }
58165
58166 if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {
58167 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. */
58168 }
58169
58170 if (doFree) {
58171 ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks);
58172 }
58173
58174 ma_audio_buffer_ref_uninit(&pAudioBuffer->ref);
58175 }
58176
58177 MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
58178 {
58179 return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);
58180 }
58181
58182 MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
58183 {
58184 return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);
58185 }
58186
58187 MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer)
58188 {
58189 ma_result result;
58190 ma_audio_buffer* pAudioBuffer;
58191 ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */
58192 ma_uint64 allocationSizeInBytes;
58193
58194 if (ppAudioBuffer == NULL) {
58195 return MA_INVALID_ARGS;
58196 }
58197
58198 *ppAudioBuffer = NULL; /* Safety. */
58199
58200 if (pConfig == NULL) {
58201 return MA_INVALID_ARGS;
58202 }
58203
58204 innerConfig = *pConfig;
58205 ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);
58206
58207 allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));
58208 if (allocationSizeInBytes > MA_SIZE_MAX) {
58209 return MA_OUT_OF_MEMORY; /* Too big. */
58210 }
58211
58212 pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */
58213 if (pAudioBuffer == NULL) {
58214 return MA_OUT_OF_MEMORY;
58215 }
58216
58217 if (pConfig->pData != NULL) {
58218 ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58219 } else {
58220 ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);
58221 }
58222
58223 innerConfig.pData = &pAudioBuffer->_pExtraData[0];
58224
58225 result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);
58226 if (result != MA_SUCCESS) {
58227 ma_free(pAudioBuffer, &innerConfig.allocationCallbacks);
58228 return result;
58229 }
58230
58231 *ppAudioBuffer = pAudioBuffer;
58232
58233 return MA_SUCCESS;
58234 }
58235
58236 MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer)
58237 {
58238 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);
58239 }
58240
58241 MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer)
58242 {
58243 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);
58244 }
58245
58246 MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
58247 {
58248 if (pAudioBuffer == NULL) {
58249 return 0;
58250 }
58251
58252 return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);
58253 }
58254
58255 MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)
58256 {
58257 if (pAudioBuffer == NULL) {
58258 return MA_INVALID_ARGS;
58259 }
58260
58261 return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);
58262 }
58263
58264 MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)
58265 {
58266 if (ppFramesOut != NULL) {
58267 *ppFramesOut = NULL; /* Safety. */
58268 }
58269
58270 if (pAudioBuffer == NULL) {
58271 if (pFrameCount != NULL) {
58272 *pFrameCount = 0;
58273 }
58274
58275 return MA_INVALID_ARGS;
58276 }
58277
58278 return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);
58279 }
58280
58281 MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount)
58282 {
58283 if (pAudioBuffer == NULL) {
58284 return MA_INVALID_ARGS;
58285 }
58286
58287 return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);
58288 }
58289
58290 MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer)
58291 {
58292 if (pAudioBuffer == NULL) {
58293 return MA_FALSE;
58294 }
58295
58296 return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);
58297 }
58298
58299 MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor)
58300 {
58301 if (pAudioBuffer == NULL) {
58302 return MA_INVALID_ARGS;
58303 }
58304
58305 return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor);
58306 }
58307
58308 MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength)
58309 {
58310 if (pAudioBuffer == NULL) {
58311 return MA_INVALID_ARGS;
58312 }
58313
58314 return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength);
58315 }
58316
58317 MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
58318 {
58319 if (pAvailableFrames == NULL) {
58320 return MA_INVALID_ARGS;
58321 }
58322
58323 *pAvailableFrames = 0;
58324
58325 if (pAudioBuffer == NULL) {
58326 return MA_INVALID_ARGS;
58327 }
58328
58329 return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);
58330 }
58331
58332
58333
58334
58335
58336 MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData)
58337 {
58338 if (pData == NULL) {
58339 return MA_INVALID_ARGS;
58340 }
58341
58342 MA_ZERO_OBJECT(pData);
58343
58344 pData->format = format;
58345 pData->channels = channels;
58346 pData->pTail = &pData->head;
58347
58348 return MA_SUCCESS;
58349 }
58350
58351 MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks)
58352 {
58353 ma_paged_audio_buffer_page* pPage;
58354
58355 if (pData == NULL) {
58356 return;
58357 }
58358
58359 /* All pages need to be freed. */
58360 pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext);
58361 while (pPage != NULL) {
58362 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext);
58363
58364 ma_free(pPage, pAllocationCallbacks);
58365 pPage = pNext;
58366 }
58367 }
58368
58369 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData)
58370 {
58371 if (pData == NULL) {
58372 return NULL;
58373 }
58374
58375 return &pData->head;
58376 }
58377
58378 MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData)
58379 {
58380 if (pData == NULL) {
58381 return NULL;
58382 }
58383
58384 return pData->pTail;
58385 }
58386
58387 MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength)
58388 {
58389 ma_paged_audio_buffer_page* pPage;
58390
58391 if (pLength == NULL) {
58392 return MA_INVALID_ARGS;
58393 }
58394
58395 *pLength = 0;
58396
58397 if (pData == NULL) {
58398 return MA_INVALID_ARGS;
58399 }
58400
58401 /* Calculate the length from the linked list. */
58402 for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) {
58403 *pLength += pPage->sizeInFrames;
58404 }
58405
58406 return MA_SUCCESS;
58407 }
58408
58409 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)
58410 {
58411 ma_paged_audio_buffer_page* pPage;
58412 ma_uint64 allocationSize;
58413
58414 if (ppPage == NULL) {
58415 return MA_INVALID_ARGS;
58416 }
58417
58418 *ppPage = NULL;
58419
58420 if (pData == NULL) {
58421 return MA_INVALID_ARGS;
58422 }
58423
58424 allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels));
58425 if (allocationSize > MA_SIZE_MAX) {
58426 return MA_OUT_OF_MEMORY; /* Too big. */
58427 }
58428
58429 pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */
58430 if (pPage == NULL) {
58431 return MA_OUT_OF_MEMORY;
58432 }
58433
58434 pPage->pNext = NULL;
58435 pPage->sizeInFrames = pageSizeInFrames;
58436
58437 if (pInitialData != NULL) {
58438 ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);
58439 }
58440
58441 *ppPage = pPage;
58442
58443 return MA_SUCCESS;
58444 }
58445
58446 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)
58447 {
58448 if (pData == NULL || pPage == NULL) {
58449 return MA_INVALID_ARGS;
58450 }
58451
58452 /* It's assumed the page is not attached to the list. */
58453 ma_free(pPage, pAllocationCallbacks);
58454
58455 return MA_SUCCESS;
58456 }
58457
58458 MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage)
58459 {
58460 if (pData == NULL || pPage == NULL) {
58461 return MA_INVALID_ARGS;
58462 }
58463
58464 /* 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. */
58465
58466 /* First thing to do is update the tail. */
58467 for (;;) {
58468 ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail);
58469 ma_paged_audio_buffer_page* pNewTail = pPage;
58470
58471 if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {
58472 /* 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. */
58473 c89atomic_exchange_ptr(&pOldTail->pNext, pPage);
58474 break; /* Done. */
58475 }
58476 }
58477
58478 return MA_SUCCESS;
58479 }
58480
58481 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)
58482 {
58483 ma_result result;
58484 ma_paged_audio_buffer_page* pPage;
58485
58486 result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);
58487 if (result != MA_SUCCESS) {
58488 return result;
58489 }
58490
58491 return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */
58492 }
58493
58494
58495 MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData)
58496 {
58497 ma_paged_audio_buffer_config config;
58498
58499 MA_ZERO_OBJECT(&config);
58500 config.pData = pData;
58501
58502 return config;
58503 }
58504
58505
58506 static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
58507 {
58508 return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
58509 }
58510
58511 static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
58512 {
58513 return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);
58514 }
58515
58516 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)
58517 {
58518 ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;
58519
58520 *pFormat = pPagedAudioBuffer->pData->format;
58521 *pChannels = pPagedAudioBuffer->pData->channels;
58522 *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
58523 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels);
58524
58525 return MA_SUCCESS;
58526 }
58527
58528 static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
58529 {
58530 return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor);
58531 }
58532
58533 static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
58534 {
58535 return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength);
58536 }
58537
58538 static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =
58539 {
58540 ma_paged_audio_buffer__data_source_on_read,
58541 ma_paged_audio_buffer__data_source_on_seek,
58542 ma_paged_audio_buffer__data_source_on_get_data_format,
58543 ma_paged_audio_buffer__data_source_on_get_cursor,
58544 ma_paged_audio_buffer__data_source_on_get_length,
58545 NULL, /* onSetLooping */
58546 0
58547 };
58548
58549 MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer)
58550 {
58551 ma_result result;
58552 ma_data_source_config dataSourceConfig;
58553
58554 if (pPagedAudioBuffer == NULL) {
58555 return MA_INVALID_ARGS;
58556 }
58557
58558 MA_ZERO_OBJECT(pPagedAudioBuffer);
58559
58560 /* A config is required for the format and channel count. */
58561 if (pConfig == NULL) {
58562 return MA_INVALID_ARGS;
58563 }
58564
58565 if (pConfig->pData == NULL) {
58566 return MA_INVALID_ARGS; /* No underlying data specified. */
58567 }
58568
58569 dataSourceConfig = ma_data_source_config_init();
58570 dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;
58571
58572 result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);
58573 if (result != MA_SUCCESS) {
58574 return result;
58575 }
58576
58577 pPagedAudioBuffer->pData = pConfig->pData;
58578 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData);
58579 pPagedAudioBuffer->relativeCursor = 0;
58580 pPagedAudioBuffer->absoluteCursor = 0;
58581
58582 return MA_SUCCESS;
58583 }
58584
58585 MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer)
58586 {
58587 if (pPagedAudioBuffer == NULL) {
58588 return;
58589 }
58590
58591 /* Nothing to do. The data needs to be deleted separately. */
58592 }
58593
58594 MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
58595 {
58596 ma_result result = MA_SUCCESS;
58597 ma_uint64 totalFramesRead = 0;
58598 ma_format format;
58599 ma_uint32 channels;
58600
58601 if (pPagedAudioBuffer == NULL) {
58602 return MA_INVALID_ARGS;
58603 }
58604
58605 format = pPagedAudioBuffer->pData->format;
58606 channels = pPagedAudioBuffer->pData->channels;
58607
58608 while (totalFramesRead < frameCount) {
58609 /* Read from the current page. The buffer should never be in a state where this is NULL. */
58610 ma_uint64 framesRemainingInCurrentPage;
58611 ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;
58612 ma_uint64 framesToReadThisIteration;
58613
58614 MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);
58615
58616 framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;
58617
58618 framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);
58619 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);
58620 totalFramesRead += framesToReadThisIteration;
58621
58622 pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;
58623 pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;
58624
58625 /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */
58626 MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);
58627
58628 if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {
58629 /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */
58630 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext);
58631 if (pNext == NULL) {
58632 result = MA_AT_END;
58633 break; /* We've reached the end. */
58634 } else {
58635 pPagedAudioBuffer->pCurrent = pNext;
58636 pPagedAudioBuffer->relativeCursor = 0;
58637 }
58638 }
58639 }
58640
58641 if (pFramesRead != NULL) {
58642 *pFramesRead = totalFramesRead;
58643 }
58644
58645 return result;
58646 }
58647
58648 MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex)
58649 {
58650 if (pPagedAudioBuffer == NULL) {
58651 return MA_INVALID_ARGS;
58652 }
58653
58654 if (frameIndex == pPagedAudioBuffer->absoluteCursor) {
58655 return MA_SUCCESS; /* Nothing to do. */
58656 }
58657
58658 if (frameIndex < pPagedAudioBuffer->absoluteCursor) {
58659 /* Moving backwards. Need to move the cursor back to the start, and then move forward. */
58660 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);
58661 pPagedAudioBuffer->absoluteCursor = 0;
58662 pPagedAudioBuffer->relativeCursor = 0;
58663
58664 /* Fall through to the forward seeking section below. */
58665 }
58666
58667 if (frameIndex > pPagedAudioBuffer->absoluteCursor) {
58668 /* Moving forward. */
58669 ma_paged_audio_buffer_page* pPage;
58670 ma_uint64 runningCursor = 0;
58671
58672 for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) {
58673 ma_uint64 pageRangeBeg = runningCursor;
58674 ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;
58675
58676 if (frameIndex >= pageRangeBeg) {
58677 if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */
58678 /* We found the page. */
58679 pPagedAudioBuffer->pCurrent = pPage;
58680 pPagedAudioBuffer->absoluteCursor = frameIndex;
58681 pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;
58682 return MA_SUCCESS;
58683 }
58684 }
58685
58686 runningCursor = pageRangeEnd;
58687 }
58688
58689 /* Getting here means we tried seeking too far forward. Don't change any state. */
58690 return MA_BAD_SEEK;
58691 }
58692
58693 return MA_SUCCESS;
58694 }
58695
58696 MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor)
58697 {
58698 if (pCursor == NULL) {
58699 return MA_INVALID_ARGS;
58700 }
58701
58702 *pCursor = 0; /* Safety. */
58703
58704 if (pPagedAudioBuffer == NULL) {
58705 return MA_INVALID_ARGS;
58706 }
58707
58708 *pCursor = pPagedAudioBuffer->absoluteCursor;
58709
58710 return MA_SUCCESS;
58711 }
58712
58713 MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength)
58714 {
58715 return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);
58716 }
58717
58718
58719
58720 /**************************************************************************************************************************************************************
58721
58722 VFS
58723
58724 **************************************************************************************************************************************************************/
58725 MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
58726 {
58727 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58728
58729 if (pFile == NULL) {
58730 return MA_INVALID_ARGS;
58731 }
58732
58733 *pFile = NULL;
58734
58735 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
58736 return MA_INVALID_ARGS;
58737 }
58738
58739 if (pCallbacks->onOpen == NULL) {
58740 return MA_NOT_IMPLEMENTED;
58741 }
58742
58743 return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);
58744 }
58745
58746 MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
58747 {
58748 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58749
58750 if (pFile == NULL) {
58751 return MA_INVALID_ARGS;
58752 }
58753
58754 *pFile = NULL;
58755
58756 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
58757 return MA_INVALID_ARGS;
58758 }
58759
58760 if (pCallbacks->onOpenW == NULL) {
58761 return MA_NOT_IMPLEMENTED;
58762 }
58763
58764 return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);
58765 }
58766
58767 MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
58768 {
58769 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58770
58771 if (pVFS == NULL || file == NULL) {
58772 return MA_INVALID_ARGS;
58773 }
58774
58775 if (pCallbacks->onClose == NULL) {
58776 return MA_NOT_IMPLEMENTED;
58777 }
58778
58779 return pCallbacks->onClose(pVFS, file);
58780 }
58781
58782 MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
58783 {
58784 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58785 ma_result result;
58786 size_t bytesRead = 0;
58787
58788 if (pBytesRead != NULL) {
58789 *pBytesRead = 0;
58790 }
58791
58792 if (pVFS == NULL || file == NULL || pDst == NULL) {
58793 return MA_INVALID_ARGS;
58794 }
58795
58796 if (pCallbacks->onRead == NULL) {
58797 return MA_NOT_IMPLEMENTED;
58798 }
58799
58800 result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead);
58801
58802 if (pBytesRead != NULL) {
58803 *pBytesRead = bytesRead;
58804 }
58805
58806 if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) {
58807 result = MA_AT_END;
58808 }
58809
58810 return result;
58811 }
58812
58813 MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
58814 {
58815 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58816
58817 if (pBytesWritten != NULL) {
58818 *pBytesWritten = 0;
58819 }
58820
58821 if (pVFS == NULL || file == NULL || pSrc == NULL) {
58822 return MA_INVALID_ARGS;
58823 }
58824
58825 if (pCallbacks->onWrite == NULL) {
58826 return MA_NOT_IMPLEMENTED;
58827 }
58828
58829 return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
58830 }
58831
58832 MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
58833 {
58834 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58835
58836 if (pVFS == NULL || file == NULL) {
58837 return MA_INVALID_ARGS;
58838 }
58839
58840 if (pCallbacks->onSeek == NULL) {
58841 return MA_NOT_IMPLEMENTED;
58842 }
58843
58844 return pCallbacks->onSeek(pVFS, file, offset, origin);
58845 }
58846
58847 MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
58848 {
58849 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58850
58851 if (pCursor == NULL) {
58852 return MA_INVALID_ARGS;
58853 }
58854
58855 *pCursor = 0;
58856
58857 if (pVFS == NULL || file == NULL) {
58858 return MA_INVALID_ARGS;
58859 }
58860
58861 if (pCallbacks->onTell == NULL) {
58862 return MA_NOT_IMPLEMENTED;
58863 }
58864
58865 return pCallbacks->onTell(pVFS, file, pCursor);
58866 }
58867
58868 MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
58869 {
58870 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
58871
58872 if (pInfo == NULL) {
58873 return MA_INVALID_ARGS;
58874 }
58875
58876 MA_ZERO_OBJECT(pInfo);
58877
58878 if (pVFS == NULL || file == NULL) {
58879 return MA_INVALID_ARGS;
58880 }
58881
58882 if (pCallbacks->onInfo == NULL) {
58883 return MA_NOT_IMPLEMENTED;
58884 }
58885
58886 return pCallbacks->onInfo(pVFS, file, pInfo);
58887 }
58888
58889
58890 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)
58891 {
58892 ma_result result;
58893 ma_vfs_file file;
58894 ma_file_info info;
58895 void* pData;
58896 size_t bytesRead;
58897
58898 if (ppData != NULL) {
58899 *ppData = NULL;
58900 }
58901 if (pSize != NULL) {
58902 *pSize = 0;
58903 }
58904
58905 if (ppData == NULL) {
58906 return MA_INVALID_ARGS;
58907 }
58908
58909 if (pFilePath != NULL) {
58910 result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
58911 } else {
58912 result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);
58913 }
58914 if (result != MA_SUCCESS) {
58915 return result;
58916 }
58917
58918 result = ma_vfs_info(pVFS, file, &info);
58919 if (result != MA_SUCCESS) {
58920 ma_vfs_close(pVFS, file);
58921 return result;
58922 }
58923
58924 if (info.sizeInBytes > MA_SIZE_MAX) {
58925 ma_vfs_close(pVFS, file);
58926 return MA_TOO_BIG;
58927 }
58928
58929 pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */
58930 if (pData == NULL) {
58931 ma_vfs_close(pVFS, file);
58932 return result;
58933 }
58934
58935 result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */
58936 ma_vfs_close(pVFS, file);
58937
58938 if (result != MA_SUCCESS) {
58939 ma_free(pData, pAllocationCallbacks);
58940 return result;
58941 }
58942
58943 if (pSize != NULL) {
58944 *pSize = bytesRead;
58945 }
58946
58947 MA_ASSERT(ppData != NULL);
58948 *ppData = pData;
58949
58950 return MA_SUCCESS;
58951 }
58952
58953 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)
58954 {
58955 return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks);
58956 }
58957
58958 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)
58959 {
58960 return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks);
58961 }
58962
58963
58964 #if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX))
58965 #define MA_USE_WIN32_FILEIO
58966 #endif
58967
58968 #if defined(MA_USE_WIN32_FILEIO)
58969 static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
58970 {
58971 *pDesiredAccess = 0;
58972 if ((openMode & MA_OPEN_MODE_READ) != 0) {
58973 *pDesiredAccess |= GENERIC_READ;
58974 }
58975 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
58976 *pDesiredAccess |= GENERIC_WRITE;
58977 }
58978
58979 *pShareMode = 0;
58980 if ((openMode & MA_OPEN_MODE_READ) != 0) {
58981 *pShareMode |= FILE_SHARE_READ;
58982 }
58983
58984 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
58985 *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */
58986 } else {
58987 *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */
58988 }
58989 }
58990
58991 static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
58992 {
58993 HANDLE hFile;
58994 DWORD dwDesiredAccess;
58995 DWORD dwShareMode;
58996 DWORD dwCreationDisposition;
58997
58998 (void)pVFS;
58999
59000 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
59001
59002 hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
59003 if (hFile == INVALID_HANDLE_VALUE) {
59004 return ma_result_from_GetLastError(GetLastError());
59005 }
59006
59007 *pFile = hFile;
59008 return MA_SUCCESS;
59009 }
59010
59011 static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59012 {
59013 HANDLE hFile;
59014 DWORD dwDesiredAccess;
59015 DWORD dwShareMode;
59016 DWORD dwCreationDisposition;
59017
59018 (void)pVFS;
59019
59020 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
59021
59022 hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
59023 if (hFile == INVALID_HANDLE_VALUE) {
59024 return ma_result_from_GetLastError(GetLastError());
59025 }
59026
59027 *pFile = hFile;
59028 return MA_SUCCESS;
59029 }
59030
59031 static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)
59032 {
59033 (void)pVFS;
59034
59035 if (CloseHandle((HANDLE)file) == 0) {
59036 return ma_result_from_GetLastError(GetLastError());
59037 }
59038
59039 return MA_SUCCESS;
59040 }
59041
59042
59043 static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59044 {
59045 ma_result result = MA_SUCCESS;
59046 size_t totalBytesRead;
59047
59048 (void)pVFS;
59049
59050 totalBytesRead = 0;
59051 while (totalBytesRead < sizeInBytes) {
59052 size_t bytesRemaining;
59053 DWORD bytesToRead;
59054 DWORD bytesRead;
59055 BOOL readResult;
59056
59057 bytesRemaining = sizeInBytes - totalBytesRead;
59058 if (bytesRemaining >= 0xFFFFFFFF) {
59059 bytesToRead = 0xFFFFFFFF;
59060 } else {
59061 bytesToRead = (DWORD)bytesRemaining;
59062 }
59063
59064 readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);
59065 if (readResult == 1 && bytesRead == 0) {
59066 result = MA_AT_END;
59067 break; /* EOF */
59068 }
59069
59070 totalBytesRead += bytesRead;
59071
59072 if (bytesRead < bytesToRead) {
59073 break; /* EOF */
59074 }
59075
59076 if (readResult == 0) {
59077 result = ma_result_from_GetLastError(GetLastError());
59078 break;
59079 }
59080 }
59081
59082 if (pBytesRead != NULL) {
59083 *pBytesRead = totalBytesRead;
59084 }
59085
59086 return result;
59087 }
59088
59089 static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
59090 {
59091 ma_result result = MA_SUCCESS;
59092 size_t totalBytesWritten;
59093
59094 (void)pVFS;
59095
59096 totalBytesWritten = 0;
59097 while (totalBytesWritten < sizeInBytes) {
59098 size_t bytesRemaining;
59099 DWORD bytesToWrite;
59100 DWORD bytesWritten;
59101 BOOL writeResult;
59102
59103 bytesRemaining = sizeInBytes - totalBytesWritten;
59104 if (bytesRemaining >= 0xFFFFFFFF) {
59105 bytesToWrite = 0xFFFFFFFF;
59106 } else {
59107 bytesToWrite = (DWORD)bytesRemaining;
59108 }
59109
59110 writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);
59111 totalBytesWritten += bytesWritten;
59112
59113 if (writeResult == 0) {
59114 result = ma_result_from_GetLastError(GetLastError());
59115 break;
59116 }
59117 }
59118
59119 if (pBytesWritten != NULL) {
59120 *pBytesWritten = totalBytesWritten;
59121 }
59122
59123 return result;
59124 }
59125
59126
59127 static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59128 {
59129 LARGE_INTEGER liDistanceToMove;
59130 DWORD dwMoveMethod;
59131 BOOL result;
59132
59133 (void)pVFS;
59134
59135 liDistanceToMove.QuadPart = offset;
59136
59137 /* */ if (origin == ma_seek_origin_current) {
59138 dwMoveMethod = FILE_CURRENT;
59139 } else if (origin == ma_seek_origin_end) {
59140 dwMoveMethod = FILE_END;
59141 } else {
59142 dwMoveMethod = FILE_BEGIN;
59143 }
59144
59145 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
59146 /* No SetFilePointerEx() so restrict to 31 bits. */
59147 if (origin > 0x7FFFFFFF) {
59148 return MA_OUT_OF_RANGE;
59149 }
59150
59151 result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
59152 #else
59153 result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
59154 #endif
59155 if (result == 0) {
59156 return ma_result_from_GetLastError(GetLastError());
59157 }
59158
59159 return MA_SUCCESS;
59160 }
59161
59162 static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59163 {
59164 LARGE_INTEGER liZero;
59165 LARGE_INTEGER liTell;
59166 BOOL result;
59167 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
59168 LONG tell;
59169 #endif
59170
59171 (void)pVFS;
59172
59173 liZero.QuadPart = 0;
59174
59175 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__)
59176 result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
59177 liTell.QuadPart = tell;
59178 #else
59179 result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
59180 #endif
59181 if (result == 0) {
59182 return ma_result_from_GetLastError(GetLastError());
59183 }
59184
59185 if (pCursor != NULL) {
59186 *pCursor = liTell.QuadPart;
59187 }
59188
59189 return MA_SUCCESS;
59190 }
59191
59192 static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59193 {
59194 BY_HANDLE_FILE_INFORMATION fi;
59195 BOOL result;
59196
59197 (void)pVFS;
59198
59199 result = GetFileInformationByHandle((HANDLE)file, &fi);
59200 if (result == 0) {
59201 return ma_result_from_GetLastError(GetLastError());
59202 }
59203
59204 pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);
59205
59206 return MA_SUCCESS;
59207 }
59208 #else
59209 static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59210 {
59211 ma_result result;
59212 FILE* pFileStd;
59213 const char* pOpenModeStr;
59214
59215 MA_ASSERT(pFilePath != NULL);
59216 MA_ASSERT(openMode != 0);
59217 MA_ASSERT(pFile != NULL);
59218
59219 (void)pVFS;
59220
59221 if ((openMode & MA_OPEN_MODE_READ) != 0) {
59222 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
59223 pOpenModeStr = "r+";
59224 } else {
59225 pOpenModeStr = "rb";
59226 }
59227 } else {
59228 pOpenModeStr = "wb";
59229 }
59230
59231 result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);
59232 if (result != MA_SUCCESS) {
59233 return result;
59234 }
59235
59236 *pFile = pFileStd;
59237
59238 return MA_SUCCESS;
59239 }
59240
59241 static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59242 {
59243 ma_result result;
59244 FILE* pFileStd;
59245 const wchar_t* pOpenModeStr;
59246
59247 MA_ASSERT(pFilePath != NULL);
59248 MA_ASSERT(openMode != 0);
59249 MA_ASSERT(pFile != NULL);
59250
59251 (void)pVFS;
59252
59253 if ((openMode & MA_OPEN_MODE_READ) != 0) {
59254 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
59255 pOpenModeStr = L"r+";
59256 } else {
59257 pOpenModeStr = L"rb";
59258 }
59259 } else {
59260 pOpenModeStr = L"wb";
59261 }
59262
59263 result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);
59264 if (result != MA_SUCCESS) {
59265 return result;
59266 }
59267
59268 *pFile = pFileStd;
59269
59270 return MA_SUCCESS;
59271 }
59272
59273 static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)
59274 {
59275 MA_ASSERT(file != NULL);
59276
59277 (void)pVFS;
59278
59279 fclose((FILE*)file);
59280
59281 return MA_SUCCESS;
59282 }
59283
59284 static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59285 {
59286 size_t result;
59287
59288 MA_ASSERT(file != NULL);
59289 MA_ASSERT(pDst != NULL);
59290
59291 (void)pVFS;
59292
59293 result = fread(pDst, 1, sizeInBytes, (FILE*)file);
59294
59295 if (pBytesRead != NULL) {
59296 *pBytesRead = result;
59297 }
59298
59299 if (result != sizeInBytes) {
59300 if (result == 0 && feof((FILE*)file)) {
59301 return MA_AT_END;
59302 } else {
59303 return ma_result_from_errno(ferror((FILE*)file));
59304 }
59305 }
59306
59307 return MA_SUCCESS;
59308 }
59309
59310 static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
59311 {
59312 size_t result;
59313
59314 MA_ASSERT(file != NULL);
59315 MA_ASSERT(pSrc != NULL);
59316
59317 (void)pVFS;
59318
59319 result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);
59320
59321 if (pBytesWritten != NULL) {
59322 *pBytesWritten = result;
59323 }
59324
59325 if (result != sizeInBytes) {
59326 return ma_result_from_errno(ferror((FILE*)file));
59327 }
59328
59329 return MA_SUCCESS;
59330 }
59331
59332 static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59333 {
59334 int result;
59335 int whence;
59336
59337 MA_ASSERT(file != NULL);
59338
59339 (void)pVFS;
59340
59341 if (origin == ma_seek_origin_start) {
59342 whence = SEEK_SET;
59343 } else if (origin == ma_seek_origin_end) {
59344 whence = SEEK_END;
59345 } else {
59346 whence = SEEK_CUR;
59347 }
59348
59349 #if defined(_WIN32)
59350 #if defined(_MSC_VER) && _MSC_VER > 1200
59351 result = _fseeki64((FILE*)file, offset, whence);
59352 #else
59353 /* No _fseeki64() so restrict to 31 bits. */
59354 if (origin > 0x7FFFFFFF) {
59355 return MA_OUT_OF_RANGE;
59356 }
59357
59358 result = fseek((FILE*)file, (int)offset, whence);
59359 #endif
59360 #else
59361 result = fseek((FILE*)file, (long int)offset, whence);
59362 #endif
59363 if (result != 0) {
59364 return MA_ERROR;
59365 }
59366
59367 return MA_SUCCESS;
59368 }
59369
59370 static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59371 {
59372 ma_int64 result;
59373
59374 MA_ASSERT(file != NULL);
59375 MA_ASSERT(pCursor != NULL);
59376
59377 (void)pVFS;
59378
59379 #if defined(_WIN32)
59380 #if defined(_MSC_VER) && _MSC_VER > 1200
59381 result = _ftelli64((FILE*)file);
59382 #else
59383 result = ftell((FILE*)file);
59384 #endif
59385 #else
59386 result = ftell((FILE*)file);
59387 #endif
59388
59389 *pCursor = result;
59390
59391 return MA_SUCCESS;
59392 }
59393
59394 #if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)
59395 int fileno(FILE *stream);
59396 #endif
59397
59398 static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59399 {
59400 int fd;
59401 struct stat info;
59402
59403 MA_ASSERT(file != NULL);
59404 MA_ASSERT(pInfo != NULL);
59405
59406 (void)pVFS;
59407
59408 #if defined(_MSC_VER)
59409 fd = _fileno((FILE*)file);
59410 #else
59411 fd = fileno((FILE*)file);
59412 #endif
59413
59414 if (fstat(fd, &info) != 0) {
59415 return ma_result_from_errno(errno);
59416 }
59417
59418 pInfo->sizeInBytes = info.st_size;
59419
59420 return MA_SUCCESS;
59421 }
59422 #endif
59423
59424
59425 static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59426 {
59427 if (pFile == NULL) {
59428 return MA_INVALID_ARGS;
59429 }
59430
59431 *pFile = NULL;
59432
59433 if (pFilePath == NULL || openMode == 0) {
59434 return MA_INVALID_ARGS;
59435 }
59436
59437 #if defined(MA_USE_WIN32_FILEIO)
59438 return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
59439 #else
59440 return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
59441 #endif
59442 }
59443
59444 static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59445 {
59446 if (pFile == NULL) {
59447 return MA_INVALID_ARGS;
59448 }
59449
59450 *pFile = NULL;
59451
59452 if (pFilePath == NULL || openMode == 0) {
59453 return MA_INVALID_ARGS;
59454 }
59455
59456 #if defined(MA_USE_WIN32_FILEIO)
59457 return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
59458 #else
59459 return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
59460 #endif
59461 }
59462
59463 static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
59464 {
59465 if (file == NULL) {
59466 return MA_INVALID_ARGS;
59467 }
59468
59469 #if defined(MA_USE_WIN32_FILEIO)
59470 return ma_default_vfs_close__win32(pVFS, file);
59471 #else
59472 return ma_default_vfs_close__stdio(pVFS, file);
59473 #endif
59474 }
59475
59476 static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59477 {
59478 if (pBytesRead != NULL) {
59479 *pBytesRead = 0;
59480 }
59481
59482 if (file == NULL || pDst == NULL) {
59483 return MA_INVALID_ARGS;
59484 }
59485
59486 #if defined(MA_USE_WIN32_FILEIO)
59487 return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
59488 #else
59489 return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
59490 #endif
59491 }
59492
59493 static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
59494 {
59495 if (pBytesWritten != NULL) {
59496 *pBytesWritten = 0;
59497 }
59498
59499 if (file == NULL || pSrc == NULL) {
59500 return MA_INVALID_ARGS;
59501 }
59502
59503 #if defined(MA_USE_WIN32_FILEIO)
59504 return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59505 #else
59506 return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59507 #endif
59508 }
59509
59510 static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59511 {
59512 if (file == NULL) {
59513 return MA_INVALID_ARGS;
59514 }
59515
59516 #if defined(MA_USE_WIN32_FILEIO)
59517 return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
59518 #else
59519 return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
59520 #endif
59521 }
59522
59523 static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59524 {
59525 if (pCursor == NULL) {
59526 return MA_INVALID_ARGS;
59527 }
59528
59529 *pCursor = 0;
59530
59531 if (file == NULL) {
59532 return MA_INVALID_ARGS;
59533 }
59534
59535 #if defined(MA_USE_WIN32_FILEIO)
59536 return ma_default_vfs_tell__win32(pVFS, file, pCursor);
59537 #else
59538 return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
59539 #endif
59540 }
59541
59542 static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59543 {
59544 if (pInfo == NULL) {
59545 return MA_INVALID_ARGS;
59546 }
59547
59548 MA_ZERO_OBJECT(pInfo);
59549
59550 if (file == NULL) {
59551 return MA_INVALID_ARGS;
59552 }
59553
59554 #if defined(MA_USE_WIN32_FILEIO)
59555 return ma_default_vfs_info__win32(pVFS, file, pInfo);
59556 #else
59557 return ma_default_vfs_info__stdio(pVFS, file, pInfo);
59558 #endif
59559 }
59560
59561
59562 MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks)
59563 {
59564 if (pVFS == NULL) {
59565 return MA_INVALID_ARGS;
59566 }
59567
59568 pVFS->cb.onOpen = ma_default_vfs_open;
59569 pVFS->cb.onOpenW = ma_default_vfs_open_w;
59570 pVFS->cb.onClose = ma_default_vfs_close;
59571 pVFS->cb.onRead = ma_default_vfs_read;
59572 pVFS->cb.onWrite = ma_default_vfs_write;
59573 pVFS->cb.onSeek = ma_default_vfs_seek;
59574 pVFS->cb.onTell = ma_default_vfs_tell;
59575 pVFS->cb.onInfo = ma_default_vfs_info;
59576 ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);
59577
59578 return MA_SUCCESS;
59579 }
59580
59581
59582 MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59583 {
59584 if (pVFS != NULL) {
59585 return ma_vfs_open(pVFS, pFilePath, openMode, pFile);
59586 } else {
59587 return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);
59588 }
59589 }
59590
59591 MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
59592 {
59593 if (pVFS != NULL) {
59594 return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);
59595 } else {
59596 return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);
59597 }
59598 }
59599
59600 MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)
59601 {
59602 if (pVFS != NULL) {
59603 return ma_vfs_close(pVFS, file);
59604 } else {
59605 return ma_default_vfs_close(pVFS, file);
59606 }
59607 }
59608
59609 MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
59610 {
59611 if (pVFS != NULL) {
59612 return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
59613 } else {
59614 return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
59615 }
59616 }
59617
59618 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)
59619 {
59620 if (pVFS != NULL) {
59621 return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59622 } else {
59623 return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
59624 }
59625 }
59626
59627 MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
59628 {
59629 if (pVFS != NULL) {
59630 return ma_vfs_seek(pVFS, file, offset, origin);
59631 } else {
59632 return ma_default_vfs_seek(pVFS, file, offset, origin);
59633 }
59634 }
59635
59636 MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
59637 {
59638 if (pVFS != NULL) {
59639 return ma_vfs_tell(pVFS, file, pCursor);
59640 } else {
59641 return ma_default_vfs_tell(pVFS, file, pCursor);
59642 }
59643 }
59644
59645 MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
59646 {
59647 if (pVFS != NULL) {
59648 return ma_vfs_info(pVFS, file, pInfo);
59649 } else {
59650 return ma_default_vfs_info(pVFS, file, pInfo);
59651 }
59652 }
59653
59654
59655
59656 /**************************************************************************************************************************************************************
59657
59658 Decoding and Encoding Headers. These are auto-generated from a tool.
59659
59660 **************************************************************************************************************************************************************/
59661 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
59662 /* dr_wav_h begin */
59663 #ifndef dr_wav_h
59664 #define dr_wav_h
59665 #ifdef __cplusplus
59666 extern "C" {
59667 #endif
59668 #define DRWAV_STRINGIFY(x) #x
59669 #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
59670 #define DRWAV_VERSION_MAJOR 0
59671 #define DRWAV_VERSION_MINOR 13
59672 #define DRWAV_VERSION_REVISION 8
59673 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
59674 #include <stddef.h>
59675 typedef signed char drwav_int8;
59676 typedef unsigned char drwav_uint8;
59677 typedef signed short drwav_int16;
59678 typedef unsigned short drwav_uint16;
59679 typedef signed int drwav_int32;
59680 typedef unsigned int drwav_uint32;
59681 #if defined(_MSC_VER) && !defined(__clang__)
59682 typedef signed __int64 drwav_int64;
59683 typedef unsigned __int64 drwav_uint64;
59684 #else
59685 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
59686 #pragma GCC diagnostic push
59687 #pragma GCC diagnostic ignored "-Wlong-long"
59688 #if defined(__clang__)
59689 #pragma GCC diagnostic ignored "-Wc++11-long-long"
59690 #endif
59691 #endif
59692 typedef signed long long drwav_int64;
59693 typedef unsigned long long drwav_uint64;
59694 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
59695 #pragma GCC diagnostic pop
59696 #endif
59697 #endif
59698 #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__)
59699 typedef drwav_uint64 drwav_uintptr;
59700 #else
59701 typedef drwav_uint32 drwav_uintptr;
59702 #endif
59703 typedef drwav_uint8 drwav_bool8;
59704 typedef drwav_uint32 drwav_bool32;
59705 #define DRWAV_TRUE 1
59706 #define DRWAV_FALSE 0
59707 #if !defined(DRWAV_API)
59708 #if defined(DRWAV_DLL)
59709 #if defined(_WIN32)
59710 #define DRWAV_DLL_IMPORT __declspec(dllimport)
59711 #define DRWAV_DLL_EXPORT __declspec(dllexport)
59712 #define DRWAV_DLL_PRIVATE static
59713 #else
59714 #if defined(__GNUC__) && __GNUC__ >= 4
59715 #define DRWAV_DLL_IMPORT __attribute__((visibility("default")))
59716 #define DRWAV_DLL_EXPORT __attribute__((visibility("default")))
59717 #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden")))
59718 #else
59719 #define DRWAV_DLL_IMPORT
59720 #define DRWAV_DLL_EXPORT
59721 #define DRWAV_DLL_PRIVATE static
59722 #endif
59723 #endif
59724 #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
59725 #define DRWAV_API DRWAV_DLL_EXPORT
59726 #else
59727 #define DRWAV_API DRWAV_DLL_IMPORT
59728 #endif
59729 #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE
59730 #else
59731 #define DRWAV_API extern
59732 #define DRWAV_PRIVATE static
59733 #endif
59734 #endif
59735 typedef drwav_int32 drwav_result;
59736 #define DRWAV_SUCCESS 0
59737 #define DRWAV_ERROR -1
59738 #define DRWAV_INVALID_ARGS -2
59739 #define DRWAV_INVALID_OPERATION -3
59740 #define DRWAV_OUT_OF_MEMORY -4
59741 #define DRWAV_OUT_OF_RANGE -5
59742 #define DRWAV_ACCESS_DENIED -6
59743 #define DRWAV_DOES_NOT_EXIST -7
59744 #define DRWAV_ALREADY_EXISTS -8
59745 #define DRWAV_TOO_MANY_OPEN_FILES -9
59746 #define DRWAV_INVALID_FILE -10
59747 #define DRWAV_TOO_BIG -11
59748 #define DRWAV_PATH_TOO_LONG -12
59749 #define DRWAV_NAME_TOO_LONG -13
59750 #define DRWAV_NOT_DIRECTORY -14
59751 #define DRWAV_IS_DIRECTORY -15
59752 #define DRWAV_DIRECTORY_NOT_EMPTY -16
59753 #define DRWAV_END_OF_FILE -17
59754 #define DRWAV_NO_SPACE -18
59755 #define DRWAV_BUSY -19
59756 #define DRWAV_IO_ERROR -20
59757 #define DRWAV_INTERRUPT -21
59758 #define DRWAV_UNAVAILABLE -22
59759 #define DRWAV_ALREADY_IN_USE -23
59760 #define DRWAV_BAD_ADDRESS -24
59761 #define DRWAV_BAD_SEEK -25
59762 #define DRWAV_BAD_PIPE -26
59763 #define DRWAV_DEADLOCK -27
59764 #define DRWAV_TOO_MANY_LINKS -28
59765 #define DRWAV_NOT_IMPLEMENTED -29
59766 #define DRWAV_NO_MESSAGE -30
59767 #define DRWAV_BAD_MESSAGE -31
59768 #define DRWAV_NO_DATA_AVAILABLE -32
59769 #define DRWAV_INVALID_DATA -33
59770 #define DRWAV_TIMEOUT -34
59771 #define DRWAV_NO_NETWORK -35
59772 #define DRWAV_NOT_UNIQUE -36
59773 #define DRWAV_NOT_SOCKET -37
59774 #define DRWAV_NO_ADDRESS -38
59775 #define DRWAV_BAD_PROTOCOL -39
59776 #define DRWAV_PROTOCOL_UNAVAILABLE -40
59777 #define DRWAV_PROTOCOL_NOT_SUPPORTED -41
59778 #define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42
59779 #define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43
59780 #define DRWAV_SOCKET_NOT_SUPPORTED -44
59781 #define DRWAV_CONNECTION_RESET -45
59782 #define DRWAV_ALREADY_CONNECTED -46
59783 #define DRWAV_NOT_CONNECTED -47
59784 #define DRWAV_CONNECTION_REFUSED -48
59785 #define DRWAV_NO_HOST -49
59786 #define DRWAV_IN_PROGRESS -50
59787 #define DRWAV_CANCELLED -51
59788 #define DRWAV_MEMORY_ALREADY_MAPPED -52
59789 #define DRWAV_AT_END -53
59790 #define DR_WAVE_FORMAT_PCM 0x1
59791 #define DR_WAVE_FORMAT_ADPCM 0x2
59792 #define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
59793 #define DR_WAVE_FORMAT_ALAW 0x6
59794 #define DR_WAVE_FORMAT_MULAW 0x7
59795 #define DR_WAVE_FORMAT_DVI_ADPCM 0x11
59796 #define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
59797 #define DRWAV_SEQUENTIAL 0x00000001
59798 DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);
59799 DRWAV_API const char* drwav_version_string(void);
59800 typedef enum
59801 {
59802 drwav_seek_origin_start,
59803 drwav_seek_origin_current
59804 } drwav_seek_origin;
59805 typedef enum
59806 {
59807 drwav_container_riff,
59808 drwav_container_w64,
59809 drwav_container_rf64
59810 } drwav_container;
59811 typedef struct
59812 {
59813 union
59814 {
59815 drwav_uint8 fourcc[4];
59816 drwav_uint8 guid[16];
59817 } id;
59818 drwav_uint64 sizeInBytes;
59819 unsigned int paddingSize;
59820 } drwav_chunk_header;
59821 typedef struct
59822 {
59823 drwav_uint16 formatTag;
59824 drwav_uint16 channels;
59825 drwav_uint32 sampleRate;
59826 drwav_uint32 avgBytesPerSec;
59827 drwav_uint16 blockAlign;
59828 drwav_uint16 bitsPerSample;
59829 drwav_uint16 extendedSize;
59830 drwav_uint16 validBitsPerSample;
59831 drwav_uint32 channelMask;
59832 drwav_uint8 subFormat[16];
59833 } drwav_fmt;
59834 DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT);
59835 typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
59836 typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
59837 typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
59838 typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT);
59839 typedef struct
59840 {
59841 void* pUserData;
59842 void* (* onMalloc)(size_t sz, void* pUserData);
59843 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
59844 void (* onFree)(void* p, void* pUserData);
59845 } drwav_allocation_callbacks;
59846 typedef struct
59847 {
59848 const drwav_uint8* data;
59849 size_t dataSize;
59850 size_t currentReadPos;
59851 } drwav__memory_stream;
59852 typedef struct
59853 {
59854 void** ppData;
59855 size_t* pDataSize;
59856 size_t dataSize;
59857 size_t dataCapacity;
59858 size_t currentWritePos;
59859 } drwav__memory_stream_write;
59860 typedef struct
59861 {
59862 drwav_container container;
59863 drwav_uint32 format;
59864 drwav_uint32 channels;
59865 drwav_uint32 sampleRate;
59866 drwav_uint32 bitsPerSample;
59867 } drwav_data_format;
59868 typedef enum
59869 {
59870 drwav_metadata_type_none = 0,
59871 drwav_metadata_type_unknown = 1 << 0,
59872 drwav_metadata_type_smpl = 1 << 1,
59873 drwav_metadata_type_inst = 1 << 2,
59874 drwav_metadata_type_cue = 1 << 3,
59875 drwav_metadata_type_acid = 1 << 4,
59876 drwav_metadata_type_bext = 1 << 5,
59877 drwav_metadata_type_list_label = 1 << 6,
59878 drwav_metadata_type_list_note = 1 << 7,
59879 drwav_metadata_type_list_labelled_cue_region = 1 << 8,
59880 drwav_metadata_type_list_info_software = 1 << 9,
59881 drwav_metadata_type_list_info_copyright = 1 << 10,
59882 drwav_metadata_type_list_info_title = 1 << 11,
59883 drwav_metadata_type_list_info_artist = 1 << 12,
59884 drwav_metadata_type_list_info_comment = 1 << 13,
59885 drwav_metadata_type_list_info_date = 1 << 14,
59886 drwav_metadata_type_list_info_genre = 1 << 15,
59887 drwav_metadata_type_list_info_album = 1 << 16,
59888 drwav_metadata_type_list_info_tracknumber = 1 << 17,
59889 drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software
59890 | drwav_metadata_type_list_info_copyright
59891 | drwav_metadata_type_list_info_title
59892 | drwav_metadata_type_list_info_artist
59893 | drwav_metadata_type_list_info_comment
59894 | drwav_metadata_type_list_info_date
59895 | drwav_metadata_type_list_info_genre
59896 | drwav_metadata_type_list_info_album
59897 | drwav_metadata_type_list_info_tracknumber,
59898 drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label
59899 | drwav_metadata_type_list_note
59900 | drwav_metadata_type_list_labelled_cue_region,
59901 drwav_metadata_type_all = -2,
59902 drwav_metadata_type_all_including_unknown = -1
59903 } drwav_metadata_type;
59904 typedef enum
59905 {
59906 drwav_smpl_loop_type_forward = 0,
59907 drwav_smpl_loop_type_pingpong = 1,
59908 drwav_smpl_loop_type_backward = 2
59909 } drwav_smpl_loop_type;
59910 typedef struct
59911 {
59912 drwav_uint32 cuePointId;
59913 drwav_uint32 type;
59914 drwav_uint32 firstSampleByteOffset;
59915 drwav_uint32 lastSampleByteOffset;
59916 drwav_uint32 sampleFraction;
59917 drwav_uint32 playCount;
59918 } drwav_smpl_loop;
59919 typedef struct
59920 {
59921 drwav_uint32 manufacturerId;
59922 drwav_uint32 productId;
59923 drwav_uint32 samplePeriodNanoseconds;
59924 drwav_uint32 midiUnityNote;
59925 drwav_uint32 midiPitchFraction;
59926 drwav_uint32 smpteFormat;
59927 drwav_uint32 smpteOffset;
59928 drwav_uint32 sampleLoopCount;
59929 drwav_uint32 samplerSpecificDataSizeInBytes;
59930 drwav_smpl_loop* pLoops;
59931 drwav_uint8* pSamplerSpecificData;
59932 } drwav_smpl;
59933 typedef struct
59934 {
59935 drwav_int8 midiUnityNote;
59936 drwav_int8 fineTuneCents;
59937 drwav_int8 gainDecibels;
59938 drwav_int8 lowNote;
59939 drwav_int8 highNote;
59940 drwav_int8 lowVelocity;
59941 drwav_int8 highVelocity;
59942 } drwav_inst;
59943 typedef struct
59944 {
59945 drwav_uint32 id;
59946 drwav_uint32 playOrderPosition;
59947 drwav_uint8 dataChunkId[4];
59948 drwav_uint32 chunkStart;
59949 drwav_uint32 blockStart;
59950 drwav_uint32 sampleByteOffset;
59951 } drwav_cue_point;
59952 typedef struct
59953 {
59954 drwav_uint32 cuePointCount;
59955 drwav_cue_point *pCuePoints;
59956 } drwav_cue;
59957 typedef enum
59958 {
59959 drwav_acid_flag_one_shot = 1,
59960 drwav_acid_flag_root_note_set = 2,
59961 drwav_acid_flag_stretch = 4,
59962 drwav_acid_flag_disk_based = 8,
59963 drwav_acid_flag_acidizer = 16
59964 } drwav_acid_flag;
59965 typedef struct
59966 {
59967 drwav_uint32 flags;
59968 drwav_uint16 midiUnityNote;
59969 drwav_uint16 reserved1;
59970 float reserved2;
59971 drwav_uint32 numBeats;
59972 drwav_uint16 meterDenominator;
59973 drwav_uint16 meterNumerator;
59974 float tempo;
59975 } drwav_acid;
59976 typedef struct
59977 {
59978 drwav_uint32 cuePointId;
59979 drwav_uint32 stringLength;
59980 char* pString;
59981 } drwav_list_label_or_note;
59982 typedef struct
59983 {
59984 char* pDescription;
59985 char* pOriginatorName;
59986 char* pOriginatorReference;
59987 char pOriginationDate[10];
59988 char pOriginationTime[8];
59989 drwav_uint64 timeReference;
59990 drwav_uint16 version;
59991 char* pCodingHistory;
59992 drwav_uint32 codingHistorySize;
59993 drwav_uint8* pUMID;
59994 drwav_uint16 loudnessValue;
59995 drwav_uint16 loudnessRange;
59996 drwav_uint16 maxTruePeakLevel;
59997 drwav_uint16 maxMomentaryLoudness;
59998 drwav_uint16 maxShortTermLoudness;
59999 } drwav_bext;
60000 typedef struct
60001 {
60002 drwav_uint32 stringLength;
60003 char* pString;
60004 } drwav_list_info_text;
60005 typedef struct
60006 {
60007 drwav_uint32 cuePointId;
60008 drwav_uint32 sampleLength;
60009 drwav_uint8 purposeId[4];
60010 drwav_uint16 country;
60011 drwav_uint16 language;
60012 drwav_uint16 dialect;
60013 drwav_uint16 codePage;
60014 drwav_uint32 stringLength;
60015 char* pString;
60016 } drwav_list_labelled_cue_region;
60017 typedef enum
60018 {
60019 drwav_metadata_location_invalid,
60020 drwav_metadata_location_top_level,
60021 drwav_metadata_location_inside_info_list,
60022 drwav_metadata_location_inside_adtl_list
60023 } drwav_metadata_location;
60024 typedef struct
60025 {
60026 drwav_uint8 id[4];
60027 drwav_metadata_location chunkLocation;
60028 drwav_uint32 dataSizeInBytes;
60029 drwav_uint8* pData;
60030 } drwav_unknown_metadata;
60031 typedef struct
60032 {
60033 drwav_metadata_type type;
60034 union
60035 {
60036 drwav_cue cue;
60037 drwav_smpl smpl;
60038 drwav_acid acid;
60039 drwav_inst inst;
60040 drwav_bext bext;
60041 drwav_list_label_or_note labelOrNote;
60042 drwav_list_labelled_cue_region labelledCueRegion;
60043 drwav_list_info_text infoText;
60044 drwav_unknown_metadata unknown;
60045 } data;
60046 } drwav_metadata;
60047 typedef struct
60048 {
60049 drwav_read_proc onRead;
60050 drwav_write_proc onWrite;
60051 drwav_seek_proc onSeek;
60052 void* pUserData;
60053 drwav_allocation_callbacks allocationCallbacks;
60054 drwav_container container;
60055 drwav_fmt fmt;
60056 drwav_uint32 sampleRate;
60057 drwav_uint16 channels;
60058 drwav_uint16 bitsPerSample;
60059 drwav_uint16 translatedFormatTag;
60060 drwav_uint64 totalPCMFrameCount;
60061 drwav_uint64 dataChunkDataSize;
60062 drwav_uint64 dataChunkDataPos;
60063 drwav_uint64 bytesRemaining;
60064 drwav_uint64 readCursorInPCMFrames;
60065 drwav_uint64 dataChunkDataSizeTargetWrite;
60066 drwav_bool32 isSequentialWrite;
60067 drwav_metadata_type allowedMetadataTypes;
60068 drwav_metadata* pMetadata;
60069 drwav_uint32 metadataCount;
60070 drwav__memory_stream memoryStream;
60071 drwav__memory_stream_write memoryStreamWrite;
60072 struct
60073 {
60074 drwav_uint32 bytesRemainingInBlock;
60075 drwav_uint16 predictor[2];
60076 drwav_int32 delta[2];
60077 drwav_int32 cachedFrames[4];
60078 drwav_uint32 cachedFrameCount;
60079 drwav_int32 prevFrames[2][2];
60080 } msadpcm;
60081 struct
60082 {
60083 drwav_uint32 bytesRemainingInBlock;
60084 drwav_int32 predictor[2];
60085 drwav_int32 stepIndex[2];
60086 drwav_int32 cachedFrames[16];
60087 drwav_uint32 cachedFrameCount;
60088 } ima;
60089 } drwav;
60090 DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
60091 DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60092 DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60093 DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
60094 DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
60095 DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
60096 DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
60097 DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
60098 DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav);
60099 DRWAV_API drwav_result drwav_uninit(drwav* pWav);
60100 DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
60101 DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
60102 DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
60103 DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
60104 DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
60105 DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor);
60106 DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength);
60107 DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
60108 DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
60109 DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
60110 DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
60111 #ifndef DR_WAV_NO_CONVERSION_API
60112 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
60113 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
60114 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
60115 DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
60116 DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
60117 DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
60118 DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
60119 DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
60120 DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
60121 DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
60122 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
60123 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
60124 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
60125 DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
60126 DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
60127 DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
60128 DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
60129 DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
60130 DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
60131 DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
60132 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
60133 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
60134 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
60135 DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
60136 DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
60137 DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
60138 DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
60139 DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
60140 DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
60141 DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
60142 #endif
60143 #ifndef DR_WAV_NO_STDIO
60144 DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
60145 DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60146 DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
60147 DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60148 DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60149 DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60150 DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
60151 DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
60152 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
60153 DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
60154 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
60155 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
60156 #endif
60157 DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);
60158 DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60159 DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
60160 DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
60161 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
60162 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
60163 #ifndef DR_WAV_NO_CONVERSION_API
60164 DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60165 DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60166 DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60167 #ifndef DR_WAV_NO_STDIO
60168 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60169 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60170 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60171 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60172 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60173 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60174 #endif
60175 DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60176 DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60177 DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
60178 #endif
60179 DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);
60180 DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data);
60181 DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data);
60182 DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data);
60183 DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data);
60184 DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data);
60185 DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data);
60186 DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data);
60187 DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]);
60188 DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
60189 #ifdef __cplusplus
60190 }
60191 #endif
60192 #endif
60193 /* dr_wav_h end */
60194 #endif /* MA_NO_WAV */
60195
60196 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
60197 /* dr_flac_h begin */
60198 #ifndef dr_flac_h
60199 #define dr_flac_h
60200 #ifdef __cplusplus
60201 extern "C" {
60202 #endif
60203 #define DRFLAC_STRINGIFY(x) #x
60204 #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x)
60205 #define DRFLAC_VERSION_MAJOR 0
60206 #define DRFLAC_VERSION_MINOR 12
60207 #define DRFLAC_VERSION_REVISION 39
60208 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
60209 #include <stddef.h>
60210 typedef signed char drflac_int8;
60211 typedef unsigned char drflac_uint8;
60212 typedef signed short drflac_int16;
60213 typedef unsigned short drflac_uint16;
60214 typedef signed int drflac_int32;
60215 typedef unsigned int drflac_uint32;
60216 #if defined(_MSC_VER) && !defined(__clang__)
60217 typedef signed __int64 drflac_int64;
60218 typedef unsigned __int64 drflac_uint64;
60219 #else
60220 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
60221 #pragma GCC diagnostic push
60222 #pragma GCC diagnostic ignored "-Wlong-long"
60223 #if defined(__clang__)
60224 #pragma GCC diagnostic ignored "-Wc++11-long-long"
60225 #endif
60226 #endif
60227 typedef signed long long drflac_int64;
60228 typedef unsigned long long drflac_uint64;
60229 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
60230 #pragma GCC diagnostic pop
60231 #endif
60232 #endif
60233 #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__)
60234 typedef drflac_uint64 drflac_uintptr;
60235 #else
60236 typedef drflac_uint32 drflac_uintptr;
60237 #endif
60238 typedef drflac_uint8 drflac_bool8;
60239 typedef drflac_uint32 drflac_bool32;
60240 #define DRFLAC_TRUE 1
60241 #define DRFLAC_FALSE 0
60242 #if !defined(DRFLAC_API)
60243 #if defined(DRFLAC_DLL)
60244 #if defined(_WIN32)
60245 #define DRFLAC_DLL_IMPORT __declspec(dllimport)
60246 #define DRFLAC_DLL_EXPORT __declspec(dllexport)
60247 #define DRFLAC_DLL_PRIVATE static
60248 #else
60249 #if defined(__GNUC__) && __GNUC__ >= 4
60250 #define DRFLAC_DLL_IMPORT __attribute__((visibility("default")))
60251 #define DRFLAC_DLL_EXPORT __attribute__((visibility("default")))
60252 #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden")))
60253 #else
60254 #define DRFLAC_DLL_IMPORT
60255 #define DRFLAC_DLL_EXPORT
60256 #define DRFLAC_DLL_PRIVATE static
60257 #endif
60258 #endif
60259 #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION)
60260 #define DRFLAC_API DRFLAC_DLL_EXPORT
60261 #else
60262 #define DRFLAC_API DRFLAC_DLL_IMPORT
60263 #endif
60264 #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE
60265 #else
60266 #define DRFLAC_API extern
60267 #define DRFLAC_PRIVATE static
60268 #endif
60269 #endif
60270 #if defined(_MSC_VER) && _MSC_VER >= 1700
60271 #define DRFLAC_DEPRECATED __declspec(deprecated)
60272 #elif (defined(__GNUC__) && __GNUC__ >= 4)
60273 #define DRFLAC_DEPRECATED __attribute__((deprecated))
60274 #elif defined(__has_feature)
60275 #if __has_feature(attribute_deprecated)
60276 #define DRFLAC_DEPRECATED __attribute__((deprecated))
60277 #else
60278 #define DRFLAC_DEPRECATED
60279 #endif
60280 #else
60281 #define DRFLAC_DEPRECATED
60282 #endif
60283 DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision);
60284 DRFLAC_API const char* drflac_version_string(void);
60285 #ifndef DR_FLAC_BUFFER_SIZE
60286 #define DR_FLAC_BUFFER_SIZE 4096
60287 #endif
60288 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
60289 #define DRFLAC_64BIT
60290 #endif
60291 #ifdef DRFLAC_64BIT
60292 typedef drflac_uint64 drflac_cache_t;
60293 #else
60294 typedef drflac_uint32 drflac_cache_t;
60295 #endif
60296 #define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
60297 #define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
60298 #define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
60299 #define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
60300 #define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
60301 #define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5
60302 #define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
60303 #define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
60304 #define DRFLAC_PICTURE_TYPE_OTHER 0
60305 #define DRFLAC_PICTURE_TYPE_FILE_ICON 1
60306 #define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
60307 #define DRFLAC_PICTURE_TYPE_COVER_FRONT 3
60308 #define DRFLAC_PICTURE_TYPE_COVER_BACK 4
60309 #define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5
60310 #define DRFLAC_PICTURE_TYPE_MEDIA 6
60311 #define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7
60312 #define DRFLAC_PICTURE_TYPE_ARTIST 8
60313 #define DRFLAC_PICTURE_TYPE_CONDUCTOR 9
60314 #define DRFLAC_PICTURE_TYPE_BAND 10
60315 #define DRFLAC_PICTURE_TYPE_COMPOSER 11
60316 #define DRFLAC_PICTURE_TYPE_LYRICIST 12
60317 #define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13
60318 #define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14
60319 #define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
60320 #define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
60321 #define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
60322 #define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18
60323 #define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
60324 #define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
60325 typedef enum
60326 {
60327 drflac_container_native,
60328 drflac_container_ogg,
60329 drflac_container_unknown
60330 } drflac_container;
60331 typedef enum
60332 {
60333 drflac_seek_origin_start,
60334 drflac_seek_origin_current
60335 } drflac_seek_origin;
60336 typedef struct
60337 {
60338 drflac_uint64 firstPCMFrame;
60339 drflac_uint64 flacFrameOffset;
60340 drflac_uint16 pcmFrameCount;
60341 } drflac_seekpoint;
60342 typedef struct
60343 {
60344 drflac_uint16 minBlockSizeInPCMFrames;
60345 drflac_uint16 maxBlockSizeInPCMFrames;
60346 drflac_uint32 minFrameSizeInPCMFrames;
60347 drflac_uint32 maxFrameSizeInPCMFrames;
60348 drflac_uint32 sampleRate;
60349 drflac_uint8 channels;
60350 drflac_uint8 bitsPerSample;
60351 drflac_uint64 totalPCMFrameCount;
60352 drflac_uint8 md5[16];
60353 } drflac_streaminfo;
60354 typedef struct
60355 {
60356 drflac_uint32 type;
60357 const void* pRawData;
60358 drflac_uint32 rawDataSize;
60359 union
60360 {
60361 drflac_streaminfo streaminfo;
60362 struct
60363 {
60364 int unused;
60365 } padding;
60366 struct
60367 {
60368 drflac_uint32 id;
60369 const void* pData;
60370 drflac_uint32 dataSize;
60371 } application;
60372 struct
60373 {
60374 drflac_uint32 seekpointCount;
60375 const drflac_seekpoint* pSeekpoints;
60376 } seektable;
60377 struct
60378 {
60379 drflac_uint32 vendorLength;
60380 const char* vendor;
60381 drflac_uint32 commentCount;
60382 const void* pComments;
60383 } vorbis_comment;
60384 struct
60385 {
60386 char catalog[128];
60387 drflac_uint64 leadInSampleCount;
60388 drflac_bool32 isCD;
60389 drflac_uint8 trackCount;
60390 const void* pTrackData;
60391 } cuesheet;
60392 struct
60393 {
60394 drflac_uint32 type;
60395 drflac_uint32 mimeLength;
60396 const char* mime;
60397 drflac_uint32 descriptionLength;
60398 const char* description;
60399 drflac_uint32 width;
60400 drflac_uint32 height;
60401 drflac_uint32 colorDepth;
60402 drflac_uint32 indexColorCount;
60403 drflac_uint32 pictureDataSize;
60404 const drflac_uint8* pPictureData;
60405 } picture;
60406 } data;
60407 } drflac_metadata;
60408 typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
60409 typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
60410 typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
60411 typedef struct
60412 {
60413 void* pUserData;
60414 void* (* onMalloc)(size_t sz, void* pUserData);
60415 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
60416 void (* onFree)(void* p, void* pUserData);
60417 } drflac_allocation_callbacks;
60418 typedef struct
60419 {
60420 const drflac_uint8* data;
60421 size_t dataSize;
60422 size_t currentReadPos;
60423 } drflac__memory_stream;
60424 typedef struct
60425 {
60426 drflac_read_proc onRead;
60427 drflac_seek_proc onSeek;
60428 void* pUserData;
60429 size_t unalignedByteCount;
60430 drflac_cache_t unalignedCache;
60431 drflac_uint32 nextL2Line;
60432 drflac_uint32 consumedBits;
60433 drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
60434 drflac_cache_t cache;
60435 drflac_uint16 crc16;
60436 drflac_cache_t crc16Cache;
60437 drflac_uint32 crc16CacheIgnoredBytes;
60438 } drflac_bs;
60439 typedef struct
60440 {
60441 drflac_uint8 subframeType;
60442 drflac_uint8 wastedBitsPerSample;
60443 drflac_uint8 lpcOrder;
60444 drflac_int32* pSamplesS32;
60445 } drflac_subframe;
60446 typedef struct
60447 {
60448 drflac_uint64 pcmFrameNumber;
60449 drflac_uint32 flacFrameNumber;
60450 drflac_uint32 sampleRate;
60451 drflac_uint16 blockSizeInPCMFrames;
60452 drflac_uint8 channelAssignment;
60453 drflac_uint8 bitsPerSample;
60454 drflac_uint8 crc8;
60455 } drflac_frame_header;
60456 typedef struct
60457 {
60458 drflac_frame_header header;
60459 drflac_uint32 pcmFramesRemaining;
60460 drflac_subframe subframes[8];
60461 } drflac_frame;
60462 typedef struct
60463 {
60464 drflac_meta_proc onMeta;
60465 void* pUserDataMD;
60466 drflac_allocation_callbacks allocationCallbacks;
60467 drflac_uint32 sampleRate;
60468 drflac_uint8 channels;
60469 drflac_uint8 bitsPerSample;
60470 drflac_uint16 maxBlockSizeInPCMFrames;
60471 drflac_uint64 totalPCMFrameCount;
60472 drflac_container container;
60473 drflac_uint32 seekpointCount;
60474 drflac_frame currentFLACFrame;
60475 drflac_uint64 currentPCMFrame;
60476 drflac_uint64 firstFLACFramePosInBytes;
60477 drflac__memory_stream memoryStream;
60478 drflac_int32* pDecodedSamples;
60479 drflac_seekpoint* pSeekpoints;
60480 void* _oggbs;
60481 drflac_bool32 _noSeekTableSeek : 1;
60482 drflac_bool32 _noBinarySearchSeek : 1;
60483 drflac_bool32 _noBruteForceSeek : 1;
60484 drflac_bs bs;
60485 drflac_uint8 pExtraData[1];
60486 } drflac;
60487 DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
60488 DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
60489 DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
60490 DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
60491 DRFLAC_API void drflac_close(drflac* pFlac);
60492 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut);
60493 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut);
60494 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut);
60495 DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex);
60496 #ifndef DR_FLAC_NO_STDIO
60497 DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
60498 DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks);
60499 DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
60500 DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
60501 #endif
60502 DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks);
60503 DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
60504 DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60505 DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60506 DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60507 #ifndef DR_FLAC_NO_STDIO
60508 DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60509 DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60510 DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60511 #endif
60512 DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60513 DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60514 DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
60515 DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks);
60516 typedef struct
60517 {
60518 drflac_uint32 countRemaining;
60519 const char* pRunningData;
60520 } drflac_vorbis_comment_iterator;
60521 DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments);
60522 DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut);
60523 typedef struct
60524 {
60525 drflac_uint32 countRemaining;
60526 const char* pRunningData;
60527 } drflac_cuesheet_track_iterator;
60528 typedef struct
60529 {
60530 drflac_uint64 offset;
60531 drflac_uint8 index;
60532 drflac_uint8 reserved[3];
60533 } drflac_cuesheet_track_index;
60534 typedef struct
60535 {
60536 drflac_uint64 offset;
60537 drflac_uint8 trackNumber;
60538 char ISRC[12];
60539 drflac_bool8 isAudio;
60540 drflac_bool8 preEmphasis;
60541 drflac_uint8 indexCount;
60542 const drflac_cuesheet_track_index* pIndexPoints;
60543 } drflac_cuesheet_track;
60544 DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData);
60545 DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack);
60546 #ifdef __cplusplus
60547 }
60548 #endif
60549 #endif
60550 /* dr_flac_h end */
60551 #endif /* MA_NO_FLAC */
60552
60553 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
60554 /* dr_mp3_h begin */
60555 #ifndef dr_mp3_h
60556 #define dr_mp3_h
60557 #ifdef __cplusplus
60558 extern "C" {
60559 #endif
60560 #define DRMP3_STRINGIFY(x) #x
60561 #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x)
60562 #define DRMP3_VERSION_MAJOR 0
60563 #define DRMP3_VERSION_MINOR 6
60564 #define DRMP3_VERSION_REVISION 34
60565 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
60566 #include <stddef.h>
60567 typedef signed char drmp3_int8;
60568 typedef unsigned char drmp3_uint8;
60569 typedef signed short drmp3_int16;
60570 typedef unsigned short drmp3_uint16;
60571 typedef signed int drmp3_int32;
60572 typedef unsigned int drmp3_uint32;
60573 #if defined(_MSC_VER) && !defined(__clang__)
60574 typedef signed __int64 drmp3_int64;
60575 typedef unsigned __int64 drmp3_uint64;
60576 #else
60577 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
60578 #pragma GCC diagnostic push
60579 #pragma GCC diagnostic ignored "-Wlong-long"
60580 #if defined(__clang__)
60581 #pragma GCC diagnostic ignored "-Wc++11-long-long"
60582 #endif
60583 #endif
60584 typedef signed long long drmp3_int64;
60585 typedef unsigned long long drmp3_uint64;
60586 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
60587 #pragma GCC diagnostic pop
60588 #endif
60589 #endif
60590 #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__)
60591 typedef drmp3_uint64 drmp3_uintptr;
60592 #else
60593 typedef drmp3_uint32 drmp3_uintptr;
60594 #endif
60595 typedef drmp3_uint8 drmp3_bool8;
60596 typedef drmp3_uint32 drmp3_bool32;
60597 #define DRMP3_TRUE 1
60598 #define DRMP3_FALSE 0
60599 #if !defined(DRMP3_API)
60600 #if defined(DRMP3_DLL)
60601 #if defined(_WIN32)
60602 #define DRMP3_DLL_IMPORT __declspec(dllimport)
60603 #define DRMP3_DLL_EXPORT __declspec(dllexport)
60604 #define DRMP3_DLL_PRIVATE static
60605 #else
60606 #if defined(__GNUC__) && __GNUC__ >= 4
60607 #define DRMP3_DLL_IMPORT __attribute__((visibility("default")))
60608 #define DRMP3_DLL_EXPORT __attribute__((visibility("default")))
60609 #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden")))
60610 #else
60611 #define DRMP3_DLL_IMPORT
60612 #define DRMP3_DLL_EXPORT
60613 #define DRMP3_DLL_PRIVATE static
60614 #endif
60615 #endif
60616 #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
60617 #define DRMP3_API DRMP3_DLL_EXPORT
60618 #else
60619 #define DRMP3_API DRMP3_DLL_IMPORT
60620 #endif
60621 #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE
60622 #else
60623 #define DRMP3_API extern
60624 #define DRMP3_PRIVATE static
60625 #endif
60626 #endif
60627 typedef drmp3_int32 drmp3_result;
60628 #define DRMP3_SUCCESS 0
60629 #define DRMP3_ERROR -1
60630 #define DRMP3_INVALID_ARGS -2
60631 #define DRMP3_INVALID_OPERATION -3
60632 #define DRMP3_OUT_OF_MEMORY -4
60633 #define DRMP3_OUT_OF_RANGE -5
60634 #define DRMP3_ACCESS_DENIED -6
60635 #define DRMP3_DOES_NOT_EXIST -7
60636 #define DRMP3_ALREADY_EXISTS -8
60637 #define DRMP3_TOO_MANY_OPEN_FILES -9
60638 #define DRMP3_INVALID_FILE -10
60639 #define DRMP3_TOO_BIG -11
60640 #define DRMP3_PATH_TOO_LONG -12
60641 #define DRMP3_NAME_TOO_LONG -13
60642 #define DRMP3_NOT_DIRECTORY -14
60643 #define DRMP3_IS_DIRECTORY -15
60644 #define DRMP3_DIRECTORY_NOT_EMPTY -16
60645 #define DRMP3_END_OF_FILE -17
60646 #define DRMP3_NO_SPACE -18
60647 #define DRMP3_BUSY -19
60648 #define DRMP3_IO_ERROR -20
60649 #define DRMP3_INTERRUPT -21
60650 #define DRMP3_UNAVAILABLE -22
60651 #define DRMP3_ALREADY_IN_USE -23
60652 #define DRMP3_BAD_ADDRESS -24
60653 #define DRMP3_BAD_SEEK -25
60654 #define DRMP3_BAD_PIPE -26
60655 #define DRMP3_DEADLOCK -27
60656 #define DRMP3_TOO_MANY_LINKS -28
60657 #define DRMP3_NOT_IMPLEMENTED -29
60658 #define DRMP3_NO_MESSAGE -30
60659 #define DRMP3_BAD_MESSAGE -31
60660 #define DRMP3_NO_DATA_AVAILABLE -32
60661 #define DRMP3_INVALID_DATA -33
60662 #define DRMP3_TIMEOUT -34
60663 #define DRMP3_NO_NETWORK -35
60664 #define DRMP3_NOT_UNIQUE -36
60665 #define DRMP3_NOT_SOCKET -37
60666 #define DRMP3_NO_ADDRESS -38
60667 #define DRMP3_BAD_PROTOCOL -39
60668 #define DRMP3_PROTOCOL_UNAVAILABLE -40
60669 #define DRMP3_PROTOCOL_NOT_SUPPORTED -41
60670 #define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42
60671 #define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43
60672 #define DRMP3_SOCKET_NOT_SUPPORTED -44
60673 #define DRMP3_CONNECTION_RESET -45
60674 #define DRMP3_ALREADY_CONNECTED -46
60675 #define DRMP3_NOT_CONNECTED -47
60676 #define DRMP3_CONNECTION_REFUSED -48
60677 #define DRMP3_NO_HOST -49
60678 #define DRMP3_IN_PROGRESS -50
60679 #define DRMP3_CANCELLED -51
60680 #define DRMP3_MEMORY_ALREADY_MAPPED -52
60681 #define DRMP3_AT_END -53
60682 #define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
60683 #define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
60684 #ifdef _MSC_VER
60685 #define DRMP3_INLINE __forceinline
60686 #elif defined(__GNUC__)
60687 #if defined(__STRICT_ANSI__)
60688 #define DRMP3_GNUC_INLINE_HINT __inline__
60689 #else
60690 #define DRMP3_GNUC_INLINE_HINT inline
60691 #endif
60692 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
60693 #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline))
60694 #else
60695 #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT
60696 #endif
60697 #elif defined(__WATCOMC__)
60698 #define DRMP3_INLINE __inline
60699 #else
60700 #define DRMP3_INLINE
60701 #endif
60702 DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision);
60703 DRMP3_API const char* drmp3_version_string(void);
60704 typedef struct
60705 {
60706 int frame_bytes, channels, hz, layer, bitrate_kbps;
60707 } drmp3dec_frame_info;
60708 typedef struct
60709 {
60710 float mdct_overlap[2][9*32], qmf_state[15*2*32];
60711 int reserv, free_format_bytes;
60712 drmp3_uint8 header[4], reserv_buf[511];
60713 } drmp3dec;
60714 DRMP3_API void drmp3dec_init(drmp3dec *dec);
60715 DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
60716 DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples);
60717 typedef enum
60718 {
60719 drmp3_seek_origin_start,
60720 drmp3_seek_origin_current
60721 } drmp3_seek_origin;
60722 typedef struct
60723 {
60724 drmp3_uint64 seekPosInBytes;
60725 drmp3_uint64 pcmFrameIndex;
60726 drmp3_uint16 mp3FramesToDiscard;
60727 drmp3_uint16 pcmFramesToDiscard;
60728 } drmp3_seek_point;
60729 typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
60730 typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
60731 typedef struct
60732 {
60733 void* pUserData;
60734 void* (* onMalloc)(size_t sz, void* pUserData);
60735 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
60736 void (* onFree)(void* p, void* pUserData);
60737 } drmp3_allocation_callbacks;
60738 typedef struct
60739 {
60740 drmp3_uint32 channels;
60741 drmp3_uint32 sampleRate;
60742 } drmp3_config;
60743 typedef struct
60744 {
60745 drmp3dec decoder;
60746 drmp3_uint32 channels;
60747 drmp3_uint32 sampleRate;
60748 drmp3_read_proc onRead;
60749 drmp3_seek_proc onSeek;
60750 void* pUserData;
60751 drmp3_allocation_callbacks allocationCallbacks;
60752 drmp3_uint32 mp3FrameChannels;
60753 drmp3_uint32 mp3FrameSampleRate;
60754 drmp3_uint32 pcmFramesConsumedInMP3Frame;
60755 drmp3_uint32 pcmFramesRemainingInMP3Frame;
60756 drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME];
60757 drmp3_uint64 currentPCMFrame;
60758 drmp3_uint64 streamCursor;
60759 drmp3_seek_point* pSeekPoints;
60760 drmp3_uint32 seekPointCount;
60761 size_t dataSize;
60762 size_t dataCapacity;
60763 size_t dataConsumed;
60764 drmp3_uint8* pData;
60765 drmp3_bool32 atEnd : 1;
60766 struct
60767 {
60768 const drmp3_uint8* pData;
60769 size_t dataSize;
60770 size_t currentReadPos;
60771 } memory;
60772 } drmp3;
60773 DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
60774 DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
60775 #ifndef DR_MP3_NO_STDIO
60776 DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
60777 DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
60778 #endif
60779 DRMP3_API void drmp3_uninit(drmp3* pMP3);
60780 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
60781 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
60782 DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
60783 DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
60784 DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
60785 DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
60786 DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints);
60787 DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints);
60788 DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
60789 DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
60790 DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
60791 DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
60792 #ifndef DR_MP3_NO_STDIO
60793 DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
60794 DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
60795 #endif
60796 DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks);
60797 DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks);
60798 #ifdef __cplusplus
60799 }
60800 #endif
60801 #endif
60802 /* dr_mp3_h end */
60803 #endif /* MA_NO_MP3 */
60804
60805
60806 /**************************************************************************************************************************************************************
60807
60808 Decoding
60809
60810 **************************************************************************************************************************************************************/
60811 #ifndef MA_NO_DECODING
60812
60813 static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
60814 {
60815 MA_ASSERT(pDecoder != NULL);
60816
60817 return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead);
60818 }
60819
60820 static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
60821 {
60822 MA_ASSERT(pDecoder != NULL);
60823
60824 return pDecoder->onSeek(pDecoder, byteOffset, origin);
60825 }
60826
60827 static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)
60828 {
60829 MA_ASSERT(pDecoder != NULL);
60830
60831 if (pDecoder->onTell == NULL) {
60832 return MA_NOT_IMPLEMENTED;
60833 }
60834
60835 return pDecoder->onTell(pDecoder, pCursor);
60836 }
60837
60838
60839 MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount)
60840 {
60841 ma_decoding_backend_config config;
60842
60843 MA_ZERO_OBJECT(&config);
60844 config.preferredFormat = preferredFormat;
60845 config.seekPointCount = seekPointCount;
60846
60847 return config;
60848 }
60849
60850
60851 MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
60852 {
60853 ma_decoder_config config;
60854 MA_ZERO_OBJECT(&config);
60855 config.format = outputFormat;
60856 config.channels = outputChannels;
60857 config.sampleRate = outputSampleRate;
60858 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */
60859 config.encodingFormat = ma_encoding_format_unknown;
60860
60861 /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
60862
60863 return config;
60864 }
60865
60866 MA_API ma_decoder_config ma_decoder_config_init_default()
60867 {
60868 return ma_decoder_config_init(ma_format_unknown, 0, 0);
60869 }
60870
60871 MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
60872 {
60873 ma_decoder_config config;
60874 if (pConfig != NULL) {
60875 config = *pConfig;
60876 } else {
60877 MA_ZERO_OBJECT(&config);
60878 }
60879
60880 return config;
60881 }
60882
60883 static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
60884 {
60885 ma_result result;
60886 ma_data_converter_config converterConfig;
60887 ma_format internalFormat;
60888 ma_uint32 internalChannels;
60889 ma_uint32 internalSampleRate;
60890 ma_channel internalChannelMap[MA_MAX_CHANNELS];
60891
60892 MA_ASSERT(pDecoder != NULL);
60893 MA_ASSERT(pConfig != NULL);
60894
60895 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap));
60896 if (result != MA_SUCCESS) {
60897 return result; /* Failed to retrieve the internal data format. */
60898 }
60899
60900
60901 /* Make sure we're not asking for too many channels. */
60902 if (pConfig->channels > MA_MAX_CHANNELS) {
60903 return MA_INVALID_ARGS;
60904 }
60905
60906 /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
60907 if (internalChannels > MA_MAX_CHANNELS) {
60908 return MA_INVALID_ARGS;
60909 }
60910
60911
60912 /* Output format. */
60913 if (pConfig->format == ma_format_unknown) {
60914 pDecoder->outputFormat = internalFormat;
60915 } else {
60916 pDecoder->outputFormat = pConfig->format;
60917 }
60918
60919 if (pConfig->channels == 0) {
60920 pDecoder->outputChannels = internalChannels;
60921 } else {
60922 pDecoder->outputChannels = pConfig->channels;
60923 }
60924
60925 if (pConfig->sampleRate == 0) {
60926 pDecoder->outputSampleRate = internalSampleRate;
60927 } else {
60928 pDecoder->outputSampleRate = pConfig->sampleRate;
60929 }
60930
60931 converterConfig = ma_data_converter_config_init(
60932 internalFormat, pDecoder->outputFormat,
60933 internalChannels, pDecoder->outputChannels,
60934 internalSampleRate, pDecoder->outputSampleRate
60935 );
60936 converterConfig.pChannelMapIn = internalChannelMap;
60937 converterConfig.pChannelMapOut = pConfig->pChannelMap;
60938 converterConfig.channelMixMode = pConfig->channelMixMode;
60939 converterConfig.ditherMode = pConfig->ditherMode;
60940 converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
60941 converterConfig.resampling = pConfig->resampling;
60942
60943 result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter);
60944 if (result != MA_SUCCESS) {
60945 return result;
60946 }
60947
60948 /*
60949 Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll
60950 need this if the data converter does not support calculation of the required input frame count. To
60951 determine support for this we'll just run a test.
60952 */
60953 {
60954 ma_uint64 unused;
60955
60956 result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused);
60957 if (result != MA_SUCCESS) {
60958 /*
60959 We were unable to calculate the required input frame count which means we'll need to use
60960 a heap-allocated cache.
60961 */
60962 ma_uint64 inputCacheCapSizeInBytes;
60963
60964 pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);
60965
60966 /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */
60967 inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels);
60968 if (inputCacheCapSizeInBytes > MA_SIZE_MAX) {
60969 ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
60970 return MA_OUT_OF_MEMORY;
60971 }
60972
60973 pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */
60974 if (pDecoder->pInputCache == NULL) {
60975 ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
60976 return MA_OUT_OF_MEMORY;
60977 }
60978 }
60979 }
60980
60981 return MA_SUCCESS;
60982 }
60983
60984
60985
60986 static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
60987 {
60988 ma_decoder* pDecoder = (ma_decoder*)pUserData;
60989 MA_ASSERT(pDecoder != NULL);
60990
60991 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);
60992 }
60993
60994 static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)
60995 {
60996 ma_decoder* pDecoder = (ma_decoder*)pUserData;
60997 MA_ASSERT(pDecoder != NULL);
60998
60999 return ma_decoder_seek_bytes(pDecoder, offset, origin);
61000 }
61001
61002 static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)
61003 {
61004 ma_decoder* pDecoder = (ma_decoder*)pUserData;
61005 MA_ASSERT(pDecoder != NULL);
61006
61007 return ma_decoder_tell_bytes(pDecoder, pCursor);
61008 }
61009
61010
61011 static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61012 {
61013 ma_result result;
61014 ma_decoding_backend_config backendConfig;
61015 ma_data_source* pBackend;
61016
61017 MA_ASSERT(pVTable != NULL);
61018 MA_ASSERT(pConfig != NULL);
61019 MA_ASSERT(pDecoder != NULL);
61020
61021 if (pVTable->onInit == NULL) {
61022 return MA_NOT_IMPLEMENTED;
61023 }
61024
61025 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
61026
61027 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);
61028 if (result != MA_SUCCESS) {
61029 return result; /* Failed to initialize the backend from this vtable. */
61030 }
61031
61032 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
61033 pDecoder->pBackend = pBackend;
61034 pDecoder->pBackendVTable = pVTable;
61035 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
61036
61037 return MA_SUCCESS;
61038 }
61039
61040
61041
61042 static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61043 {
61044 ma_result result = MA_NO_BACKEND;
61045 size_t ivtable;
61046
61047 MA_ASSERT(pConfig != NULL);
61048 MA_ASSERT(pDecoder != NULL);
61049
61050 if (pConfig->ppCustomBackendVTables == NULL) {
61051 return MA_NO_BACKEND;
61052 }
61053
61054 /* The order each backend is listed is what defines the priority. */
61055 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
61056 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
61057 if (pVTable != NULL && pVTable->onInit != NULL) {
61058 result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder);
61059 if (result == MA_SUCCESS) {
61060 return MA_SUCCESS;
61061 } else {
61062 /* 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. */
61063 result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
61064 if (result != MA_SUCCESS) {
61065 return result; /* Failed to seek back to the start. */
61066 }
61067 }
61068 } else {
61069 /* No vtable. */
61070 }
61071 }
61072
61073 /* Getting here means we couldn't find a backend. */
61074 return MA_NO_BACKEND;
61075 }
61076
61077
61078 /* WAV */
61079 #ifdef dr_wav_h
61080 #define MA_HAS_WAV
61081
61082 typedef struct
61083 {
61084 ma_data_source_base ds;
61085 ma_read_proc onRead;
61086 ma_seek_proc onSeek;
61087 ma_tell_proc onTell;
61088 void* pReadSeekTellUserData;
61089 ma_format format; /* Can be f32, s16 or s32. */
61090 #if !defined(MA_NO_WAV)
61091 drwav dr;
61092 #endif
61093 } ma_wav;
61094
61095 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);
61096 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);
61097 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);
61098 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);
61099 MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks);
61100 MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
61101 MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex);
61102 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);
61103 MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor);
61104 MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength);
61105
61106
61107 static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61108 {
61109 return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead);
61110 }
61111
61112 static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
61113 {
61114 return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex);
61115 }
61116
61117 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)
61118 {
61119 return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
61120 }
61121
61122 static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
61123 {
61124 return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor);
61125 }
61126
61127 static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
61128 {
61129 return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength);
61130 }
61131
61132 static ma_data_source_vtable g_ma_wav_ds_vtable =
61133 {
61134 ma_wav_ds_read,
61135 ma_wav_ds_seek,
61136 ma_wav_ds_get_data_format,
61137 ma_wav_ds_get_cursor,
61138 ma_wav_ds_get_length,
61139 NULL, /* onSetLooping */
61140 0
61141 };
61142
61143
61144 #if !defined(MA_NO_WAV)
61145 static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
61146 {
61147 drwav_allocation_callbacks callbacks;
61148
61149 if (pAllocationCallbacks != NULL) {
61150 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
61151 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
61152 callbacks.onFree = pAllocationCallbacks->onFree;
61153 callbacks.pUserData = pAllocationCallbacks->pUserData;
61154 } else {
61155 callbacks.onMalloc = ma__malloc_default;
61156 callbacks.onRealloc = ma__realloc_default;
61157 callbacks.onFree = ma__free_default;
61158 callbacks.pUserData = NULL;
61159 }
61160
61161 return callbacks;
61162 }
61163
61164 static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
61165 {
61166 ma_wav* pWav = (ma_wav*)pUserData;
61167 ma_result result;
61168 size_t bytesRead;
61169
61170 MA_ASSERT(pWav != NULL);
61171
61172 result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
61173 (void)result;
61174
61175 return bytesRead;
61176 }
61177
61178 static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin)
61179 {
61180 ma_wav* pWav = (ma_wav*)pUserData;
61181 ma_result result;
61182 ma_seek_origin maSeekOrigin;
61183
61184 MA_ASSERT(pWav != NULL);
61185
61186 maSeekOrigin = ma_seek_origin_start;
61187 if (origin == drwav_seek_origin_current) {
61188 maSeekOrigin = ma_seek_origin_current;
61189 }
61190
61191 result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin);
61192 if (result != MA_SUCCESS) {
61193 return MA_FALSE;
61194 }
61195
61196 return MA_TRUE;
61197 }
61198 #endif
61199
61200 static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav)
61201 {
61202 ma_result result;
61203 ma_data_source_config dataSourceConfig;
61204
61205 if (pWav == NULL) {
61206 return MA_INVALID_ARGS;
61207 }
61208
61209 MA_ZERO_OBJECT(pWav);
61210 pWav->format = ma_format_unknown; /* Use closest match to source file by default. */
61211
61212 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
61213 pWav->format = pConfig->preferredFormat;
61214 } else {
61215 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
61216 }
61217
61218 dataSourceConfig = ma_data_source_config_init();
61219 dataSourceConfig.vtable = &g_ma_wav_ds_vtable;
61220
61221 result = ma_data_source_init(&dataSourceConfig, &pWav->ds);
61222 if (result != MA_SUCCESS) {
61223 return result; /* Failed to initialize the base data source. */
61224 }
61225
61226 return MA_SUCCESS;
61227 }
61228
61229 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)
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 (onRead == NULL || onSeek == NULL) {
61239 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
61240 }
61241
61242 pWav->onRead = onRead;
61243 pWav->onSeek = onSeek;
61244 pWav->onTell = onTell;
61245 pWav->pReadSeekTellUserData = pReadSeekTellUserData;
61246
61247 #if !defined(MA_NO_WAV)
61248 {
61249 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
61250 drwav_bool32 wavResult;
61251
61252 wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks);
61253 if (wavResult != MA_TRUE) {
61254 return MA_INVALID_FILE;
61255 }
61256
61257 /*
61258 If an explicit format was not specified, try picking the closest match based on the internal
61259 format. The format needs to be supported by miniaudio.
61260 */
61261 if (pWav->format == ma_format_unknown) {
61262 switch (pWav->dr.translatedFormatTag)
61263 {
61264 case DR_WAVE_FORMAT_PCM:
61265 {
61266 if (pWav->dr.bitsPerSample == 8) {
61267 pWav->format = ma_format_u8;
61268 } else if (pWav->dr.bitsPerSample == 16) {
61269 pWav->format = ma_format_s16;
61270 } else if (pWav->dr.bitsPerSample == 24) {
61271 pWav->format = ma_format_s24;
61272 } else if (pWav->dr.bitsPerSample == 32) {
61273 pWav->format = ma_format_s32;
61274 }
61275 } break;
61276
61277 case DR_WAVE_FORMAT_IEEE_FLOAT:
61278 {
61279 if (pWav->dr.bitsPerSample == 32) {
61280 pWav->format = ma_format_f32;
61281 }
61282 } break;
61283
61284 default: break;
61285 }
61286
61287 /* Fall back to f32 if we couldn't find anything. */
61288 if (pWav->format == ma_format_unknown) {
61289 pWav->format = ma_format_f32;
61290 }
61291 }
61292
61293 return MA_SUCCESS;
61294 }
61295 #else
61296 {
61297 /* wav is disabled. */
61298 (void)pAllocationCallbacks;
61299 return MA_NOT_IMPLEMENTED;
61300 }
61301 #endif
61302 }
61303
61304 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)
61305 {
61306 ma_result result;
61307
61308 result = ma_wav_init_internal(pConfig, pWav);
61309 if (result != MA_SUCCESS) {
61310 return result;
61311 }
61312
61313 #if !defined(MA_NO_WAV)
61314 {
61315 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
61316 drwav_bool32 wavResult;
61317
61318 wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks);
61319 if (wavResult != MA_TRUE) {
61320 return MA_INVALID_FILE;
61321 }
61322
61323 return MA_SUCCESS;
61324 }
61325 #else
61326 {
61327 /* wav is disabled. */
61328 (void)pFilePath;
61329 (void)pAllocationCallbacks;
61330 return MA_NOT_IMPLEMENTED;
61331 }
61332 #endif
61333 }
61334
61335 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)
61336 {
61337 ma_result result;
61338
61339 result = ma_wav_init_internal(pConfig, pWav);
61340 if (result != MA_SUCCESS) {
61341 return result;
61342 }
61343
61344 #if !defined(MA_NO_WAV)
61345 {
61346 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
61347 drwav_bool32 wavResult;
61348
61349 wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks);
61350 if (wavResult != MA_TRUE) {
61351 return MA_INVALID_FILE;
61352 }
61353
61354 return MA_SUCCESS;
61355 }
61356 #else
61357 {
61358 /* wav is disabled. */
61359 (void)pFilePath;
61360 (void)pAllocationCallbacks;
61361 return MA_NOT_IMPLEMENTED;
61362 }
61363 #endif
61364 }
61365
61366 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)
61367 {
61368 ma_result result;
61369
61370 result = ma_wav_init_internal(pConfig, pWav);
61371 if (result != MA_SUCCESS) {
61372 return result;
61373 }
61374
61375 #if !defined(MA_NO_WAV)
61376 {
61377 drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
61378 drwav_bool32 wavResult;
61379
61380 wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks);
61381 if (wavResult != MA_TRUE) {
61382 return MA_INVALID_FILE;
61383 }
61384
61385 return MA_SUCCESS;
61386 }
61387 #else
61388 {
61389 /* wav is disabled. */
61390 (void)pData;
61391 (void)dataSize;
61392 (void)pAllocationCallbacks;
61393 return MA_NOT_IMPLEMENTED;
61394 }
61395 #endif
61396 }
61397
61398 MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks)
61399 {
61400 if (pWav == NULL) {
61401 return;
61402 }
61403
61404 (void)pAllocationCallbacks;
61405
61406 #if !defined(MA_NO_WAV)
61407 {
61408 drwav_uninit(&pWav->dr);
61409 }
61410 #else
61411 {
61412 /* wav is disabled. Should never hit this since initialization would have failed. */
61413 MA_ASSERT(MA_FALSE);
61414 }
61415 #endif
61416
61417 ma_data_source_uninit(&pWav->ds);
61418 }
61419
61420 MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61421 {
61422 if (pFramesRead != NULL) {
61423 *pFramesRead = 0;
61424 }
61425
61426 if (frameCount == 0) {
61427 return MA_INVALID_ARGS;
61428 }
61429
61430 if (pWav == NULL) {
61431 return MA_INVALID_ARGS;
61432 }
61433
61434 #if !defined(MA_NO_WAV)
61435 {
61436 /* We always use floating point format. */
61437 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
61438 ma_uint64 totalFramesRead = 0;
61439 ma_format format;
61440
61441 ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0);
61442
61443 switch (format)
61444 {
61445 case ma_format_f32:
61446 {
61447 totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut);
61448 } break;
61449
61450 case ma_format_s16:
61451 {
61452 totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut);
61453 } break;
61454
61455 case ma_format_s32:
61456 {
61457 totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut);
61458 } break;
61459
61460 /* Fallback to a raw read. */
61461 case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */
61462 default:
61463 {
61464 totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut);
61465 } break;
61466 }
61467
61468 /* In the future we'll update dr_wav to return MA_AT_END for us. */
61469 if (totalFramesRead == 0) {
61470 result = MA_AT_END;
61471 }
61472
61473 if (pFramesRead != NULL) {
61474 *pFramesRead = totalFramesRead;
61475 }
61476
61477 if (result == MA_SUCCESS && totalFramesRead == 0) {
61478 result = MA_AT_END;
61479 }
61480
61481 return result;
61482 }
61483 #else
61484 {
61485 /* wav is disabled. Should never hit this since initialization would have failed. */
61486 MA_ASSERT(MA_FALSE);
61487
61488 (void)pFramesOut;
61489 (void)frameCount;
61490 (void)pFramesRead;
61491
61492 return MA_NOT_IMPLEMENTED;
61493 }
61494 #endif
61495 }
61496
61497 MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex)
61498 {
61499 if (pWav == NULL) {
61500 return MA_INVALID_ARGS;
61501 }
61502
61503 #if !defined(MA_NO_WAV)
61504 {
61505 drwav_bool32 wavResult;
61506
61507 wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex);
61508 if (wavResult != DRWAV_TRUE) {
61509 return MA_ERROR;
61510 }
61511
61512 return MA_SUCCESS;
61513 }
61514 #else
61515 {
61516 /* wav is disabled. Should never hit this since initialization would have failed. */
61517 MA_ASSERT(MA_FALSE);
61518
61519 (void)frameIndex;
61520
61521 return MA_NOT_IMPLEMENTED;
61522 }
61523 #endif
61524 }
61525
61526 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)
61527 {
61528 /* Defaults for safety. */
61529 if (pFormat != NULL) {
61530 *pFormat = ma_format_unknown;
61531 }
61532 if (pChannels != NULL) {
61533 *pChannels = 0;
61534 }
61535 if (pSampleRate != NULL) {
61536 *pSampleRate = 0;
61537 }
61538 if (pChannelMap != NULL) {
61539 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
61540 }
61541
61542 if (pWav == NULL) {
61543 return MA_INVALID_OPERATION;
61544 }
61545
61546 if (pFormat != NULL) {
61547 *pFormat = pWav->format;
61548 }
61549
61550 #if !defined(MA_NO_WAV)
61551 {
61552 if (pChannels != NULL) {
61553 *pChannels = pWav->dr.channels;
61554 }
61555
61556 if (pSampleRate != NULL) {
61557 *pSampleRate = pWav->dr.sampleRate;
61558 }
61559
61560 if (pChannelMap != NULL) {
61561 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels);
61562 }
61563
61564 return MA_SUCCESS;
61565 }
61566 #else
61567 {
61568 /* wav is disabled. Should never hit this since initialization would have failed. */
61569 MA_ASSERT(MA_FALSE);
61570 return MA_NOT_IMPLEMENTED;
61571 }
61572 #endif
61573 }
61574
61575 MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor)
61576 {
61577 if (pCursor == NULL) {
61578 return MA_INVALID_ARGS;
61579 }
61580
61581 *pCursor = 0; /* Safety. */
61582
61583 if (pWav == NULL) {
61584 return MA_INVALID_ARGS;
61585 }
61586
61587 #if !defined(MA_NO_WAV)
61588 {
61589 drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor);
61590 if (wavResult != DRWAV_SUCCESS) {
61591 return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */
61592 }
61593
61594 return MA_SUCCESS;
61595 }
61596 #else
61597 {
61598 /* wav is disabled. Should never hit this since initialization would have failed. */
61599 MA_ASSERT(MA_FALSE);
61600 return MA_NOT_IMPLEMENTED;
61601 }
61602 #endif
61603 }
61604
61605 MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength)
61606 {
61607 if (pLength == NULL) {
61608 return MA_INVALID_ARGS;
61609 }
61610
61611 *pLength = 0; /* Safety. */
61612
61613 if (pWav == NULL) {
61614 return MA_INVALID_ARGS;
61615 }
61616
61617 #if !defined(MA_NO_WAV)
61618 {
61619 drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength);
61620 if (wavResult != DRWAV_SUCCESS) {
61621 return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */
61622 }
61623
61624 return MA_SUCCESS;
61625 }
61626 #else
61627 {
61628 /* wav is disabled. Should never hit this since initialization would have failed. */
61629 MA_ASSERT(MA_FALSE);
61630 return MA_NOT_IMPLEMENTED;
61631 }
61632 #endif
61633 }
61634
61635
61636 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)
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(onRead, onSeek, onTell, pReadSeekTellUserData, 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 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)
61661 {
61662 ma_result result;
61663 ma_wav* pWav;
61664
61665 (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. */
61666
61667 /* For now we're just allocating the decoder backend on the heap. */
61668 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
61669 if (pWav == NULL) {
61670 return MA_OUT_OF_MEMORY;
61671 }
61672
61673 result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav);
61674 if (result != MA_SUCCESS) {
61675 ma_free(pWav, pAllocationCallbacks);
61676 return result;
61677 }
61678
61679 *ppBackend = pWav;
61680
61681 return MA_SUCCESS;
61682 }
61683
61684 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)
61685 {
61686 ma_result result;
61687 ma_wav* pWav;
61688
61689 (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. */
61690
61691 /* For now we're just allocating the decoder backend on the heap. */
61692 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
61693 if (pWav == NULL) {
61694 return MA_OUT_OF_MEMORY;
61695 }
61696
61697 result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav);
61698 if (result != MA_SUCCESS) {
61699 ma_free(pWav, pAllocationCallbacks);
61700 return result;
61701 }
61702
61703 *ppBackend = pWav;
61704
61705 return MA_SUCCESS;
61706 }
61707
61708 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)
61709 {
61710 ma_result result;
61711 ma_wav* pWav;
61712
61713 (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. */
61714
61715 /* For now we're just allocating the decoder backend on the heap. */
61716 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
61717 if (pWav == NULL) {
61718 return MA_OUT_OF_MEMORY;
61719 }
61720
61721 result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav);
61722 if (result != MA_SUCCESS) {
61723 ma_free(pWav, pAllocationCallbacks);
61724 return result;
61725 }
61726
61727 *ppBackend = pWav;
61728
61729 return MA_SUCCESS;
61730 }
61731
61732 static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
61733 {
61734 ma_wav* pWav = (ma_wav*)pBackend;
61735
61736 (void)pUserData;
61737
61738 ma_wav_uninit(pWav, pAllocationCallbacks);
61739 ma_free(pWav, pAllocationCallbacks);
61740 }
61741
61742 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav =
61743 {
61744 ma_decoding_backend_init__wav,
61745 ma_decoding_backend_init_file__wav,
61746 ma_decoding_backend_init_file_w__wav,
61747 ma_decoding_backend_init_memory__wav,
61748 ma_decoding_backend_uninit__wav
61749 };
61750
61751 static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
61752 {
61753 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder);
61754 }
61755 #endif /* dr_wav_h */
61756
61757 /* FLAC */
61758 #ifdef dr_flac_h
61759 #define MA_HAS_FLAC
61760
61761 typedef struct
61762 {
61763 ma_data_source_base ds;
61764 ma_read_proc onRead;
61765 ma_seek_proc onSeek;
61766 ma_tell_proc onTell;
61767 void* pReadSeekTellUserData;
61768 ma_format format; /* Can be f32, s16 or s32. */
61769 #if !defined(MA_NO_FLAC)
61770 drflac* dr;
61771 #endif
61772 } ma_flac;
61773
61774 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);
61775 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);
61776 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);
61777 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);
61778 MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks);
61779 MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
61780 MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex);
61781 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);
61782 MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor);
61783 MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength);
61784
61785
61786 static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
61787 {
61788 return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead);
61789 }
61790
61791 static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
61792 {
61793 return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex);
61794 }
61795
61796 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)
61797 {
61798 return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
61799 }
61800
61801 static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
61802 {
61803 return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor);
61804 }
61805
61806 static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
61807 {
61808 return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength);
61809 }
61810
61811 static ma_data_source_vtable g_ma_flac_ds_vtable =
61812 {
61813 ma_flac_ds_read,
61814 ma_flac_ds_seek,
61815 ma_flac_ds_get_data_format,
61816 ma_flac_ds_get_cursor,
61817 ma_flac_ds_get_length,
61818 NULL, /* onSetLooping */
61819 0
61820 };
61821
61822
61823 #if !defined(MA_NO_FLAC)
61824 static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
61825 {
61826 drflac_allocation_callbacks callbacks;
61827
61828 if (pAllocationCallbacks != NULL) {
61829 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
61830 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
61831 callbacks.onFree = pAllocationCallbacks->onFree;
61832 callbacks.pUserData = pAllocationCallbacks->pUserData;
61833 } else {
61834 callbacks.onMalloc = ma__malloc_default;
61835 callbacks.onRealloc = ma__realloc_default;
61836 callbacks.onFree = ma__free_default;
61837 callbacks.pUserData = NULL;
61838 }
61839
61840 return callbacks;
61841 }
61842
61843 static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
61844 {
61845 ma_flac* pFlac = (ma_flac*)pUserData;
61846 ma_result result;
61847 size_t bytesRead;
61848
61849 MA_ASSERT(pFlac != NULL);
61850
61851 result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
61852 (void)result;
61853
61854 return bytesRead;
61855 }
61856
61857 static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin)
61858 {
61859 ma_flac* pFlac = (ma_flac*)pUserData;
61860 ma_result result;
61861 ma_seek_origin maSeekOrigin;
61862
61863 MA_ASSERT(pFlac != NULL);
61864
61865 maSeekOrigin = ma_seek_origin_start;
61866 if (origin == drflac_seek_origin_current) {
61867 maSeekOrigin = ma_seek_origin_current;
61868 }
61869
61870 result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin);
61871 if (result != MA_SUCCESS) {
61872 return MA_FALSE;
61873 }
61874
61875 return MA_TRUE;
61876 }
61877 #endif
61878
61879 static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac)
61880 {
61881 ma_result result;
61882 ma_data_source_config dataSourceConfig;
61883
61884 if (pFlac == NULL) {
61885 return MA_INVALID_ARGS;
61886 }
61887
61888 MA_ZERO_OBJECT(pFlac);
61889 pFlac->format = ma_format_f32; /* f32 by default. */
61890
61891 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
61892 pFlac->format = pConfig->preferredFormat;
61893 } else {
61894 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
61895 }
61896
61897 dataSourceConfig = ma_data_source_config_init();
61898 dataSourceConfig.vtable = &g_ma_flac_ds_vtable;
61899
61900 result = ma_data_source_init(&dataSourceConfig, &pFlac->ds);
61901 if (result != MA_SUCCESS) {
61902 return result; /* Failed to initialize the base data source. */
61903 }
61904
61905 return MA_SUCCESS;
61906 }
61907
61908 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)
61909 {
61910 ma_result result;
61911
61912 result = ma_flac_init_internal(pConfig, pFlac);
61913 if (result != MA_SUCCESS) {
61914 return result;
61915 }
61916
61917 if (onRead == NULL || onSeek == NULL) {
61918 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
61919 }
61920
61921 pFlac->onRead = onRead;
61922 pFlac->onSeek = onSeek;
61923 pFlac->onTell = onTell;
61924 pFlac->pReadSeekTellUserData = pReadSeekTellUserData;
61925
61926 #if !defined(MA_NO_FLAC)
61927 {
61928 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
61929
61930 pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks);
61931 if (pFlac->dr == NULL) {
61932 return MA_INVALID_FILE;
61933 }
61934
61935 return MA_SUCCESS;
61936 }
61937 #else
61938 {
61939 /* flac is disabled. */
61940 (void)pAllocationCallbacks;
61941 return MA_NOT_IMPLEMENTED;
61942 }
61943 #endif
61944 }
61945
61946 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)
61947 {
61948 ma_result result;
61949
61950 result = ma_flac_init_internal(pConfig, pFlac);
61951 if (result != MA_SUCCESS) {
61952 return result;
61953 }
61954
61955 #if !defined(MA_NO_FLAC)
61956 {
61957 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
61958
61959 pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks);
61960 if (pFlac->dr == NULL) {
61961 return MA_INVALID_FILE;
61962 }
61963
61964 return MA_SUCCESS;
61965 }
61966 #else
61967 {
61968 /* flac is disabled. */
61969 (void)pFilePath;
61970 (void)pAllocationCallbacks;
61971 return MA_NOT_IMPLEMENTED;
61972 }
61973 #endif
61974 }
61975
61976 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)
61977 {
61978 ma_result result;
61979
61980 result = ma_flac_init_internal(pConfig, pFlac);
61981 if (result != MA_SUCCESS) {
61982 return result;
61983 }
61984
61985 #if !defined(MA_NO_FLAC)
61986 {
61987 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
61988
61989 pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks);
61990 if (pFlac->dr == NULL) {
61991 return MA_INVALID_FILE;
61992 }
61993
61994 return MA_SUCCESS;
61995 }
61996 #else
61997 {
61998 /* flac is disabled. */
61999 (void)pFilePath;
62000 (void)pAllocationCallbacks;
62001 return MA_NOT_IMPLEMENTED;
62002 }
62003 #endif
62004 }
62005
62006 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)
62007 {
62008 ma_result result;
62009
62010 result = ma_flac_init_internal(pConfig, pFlac);
62011 if (result != MA_SUCCESS) {
62012 return result;
62013 }
62014
62015 #if !defined(MA_NO_FLAC)
62016 {
62017 drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
62018
62019 pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks);
62020 if (pFlac->dr == NULL) {
62021 return MA_INVALID_FILE;
62022 }
62023
62024 return MA_SUCCESS;
62025 }
62026 #else
62027 {
62028 /* flac is disabled. */
62029 (void)pData;
62030 (void)dataSize;
62031 (void)pAllocationCallbacks;
62032 return MA_NOT_IMPLEMENTED;
62033 }
62034 #endif
62035 }
62036
62037 MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks)
62038 {
62039 if (pFlac == NULL) {
62040 return;
62041 }
62042
62043 (void)pAllocationCallbacks;
62044
62045 #if !defined(MA_NO_FLAC)
62046 {
62047 drflac_close(pFlac->dr);
62048 }
62049 #else
62050 {
62051 /* flac is disabled. Should never hit this since initialization would have failed. */
62052 MA_ASSERT(MA_FALSE);
62053 }
62054 #endif
62055
62056 ma_data_source_uninit(&pFlac->ds);
62057 }
62058
62059 MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
62060 {
62061 if (pFramesRead != NULL) {
62062 *pFramesRead = 0;
62063 }
62064
62065 if (frameCount == 0) {
62066 return MA_INVALID_ARGS;
62067 }
62068
62069 if (pFlac == NULL) {
62070 return MA_INVALID_ARGS;
62071 }
62072
62073 #if !defined(MA_NO_FLAC)
62074 {
62075 /* We always use floating point format. */
62076 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
62077 ma_uint64 totalFramesRead = 0;
62078 ma_format format;
62079
62080 ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0);
62081
62082 switch (format)
62083 {
62084 case ma_format_f32:
62085 {
62086 totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut);
62087 } break;
62088
62089 case ma_format_s16:
62090 {
62091 totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut);
62092 } break;
62093
62094 case ma_format_s32:
62095 {
62096 totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut);
62097 } break;
62098
62099 case ma_format_u8:
62100 case ma_format_s24:
62101 case ma_format_unknown:
62102 default:
62103 {
62104 return MA_INVALID_OPERATION;
62105 };
62106 }
62107
62108 /* In the future we'll update dr_flac to return MA_AT_END for us. */
62109 if (totalFramesRead == 0) {
62110 result = MA_AT_END;
62111 }
62112
62113 if (pFramesRead != NULL) {
62114 *pFramesRead = totalFramesRead;
62115 }
62116
62117 if (result == MA_SUCCESS && totalFramesRead == 0) {
62118 result = MA_AT_END;
62119 }
62120
62121 return result;
62122 }
62123 #else
62124 {
62125 /* flac is disabled. Should never hit this since initialization would have failed. */
62126 MA_ASSERT(MA_FALSE);
62127
62128 (void)pFramesOut;
62129 (void)frameCount;
62130 (void)pFramesRead;
62131
62132 return MA_NOT_IMPLEMENTED;
62133 }
62134 #endif
62135 }
62136
62137 MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex)
62138 {
62139 if (pFlac == NULL) {
62140 return MA_INVALID_ARGS;
62141 }
62142
62143 #if !defined(MA_NO_FLAC)
62144 {
62145 drflac_bool32 flacResult;
62146
62147 flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex);
62148 if (flacResult != DRFLAC_TRUE) {
62149 return MA_ERROR;
62150 }
62151
62152 return MA_SUCCESS;
62153 }
62154 #else
62155 {
62156 /* flac is disabled. Should never hit this since initialization would have failed. */
62157 MA_ASSERT(MA_FALSE);
62158
62159 (void)frameIndex;
62160
62161 return MA_NOT_IMPLEMENTED;
62162 }
62163 #endif
62164 }
62165
62166 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)
62167 {
62168 /* Defaults for safety. */
62169 if (pFormat != NULL) {
62170 *pFormat = ma_format_unknown;
62171 }
62172 if (pChannels != NULL) {
62173 *pChannels = 0;
62174 }
62175 if (pSampleRate != NULL) {
62176 *pSampleRate = 0;
62177 }
62178 if (pChannelMap != NULL) {
62179 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
62180 }
62181
62182 if (pFlac == NULL) {
62183 return MA_INVALID_OPERATION;
62184 }
62185
62186 if (pFormat != NULL) {
62187 *pFormat = pFlac->format;
62188 }
62189
62190 #if !defined(MA_NO_FLAC)
62191 {
62192 if (pChannels != NULL) {
62193 *pChannels = pFlac->dr->channels;
62194 }
62195
62196 if (pSampleRate != NULL) {
62197 *pSampleRate = pFlac->dr->sampleRate;
62198 }
62199
62200 if (pChannelMap != NULL) {
62201 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels);
62202 }
62203
62204 return MA_SUCCESS;
62205 }
62206 #else
62207 {
62208 /* flac is disabled. Should never hit this since initialization would have failed. */
62209 MA_ASSERT(MA_FALSE);
62210 return MA_NOT_IMPLEMENTED;
62211 }
62212 #endif
62213 }
62214
62215 MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor)
62216 {
62217 if (pCursor == NULL) {
62218 return MA_INVALID_ARGS;
62219 }
62220
62221 *pCursor = 0; /* Safety. */
62222
62223 if (pFlac == NULL) {
62224 return MA_INVALID_ARGS;
62225 }
62226
62227 #if !defined(MA_NO_FLAC)
62228 {
62229 *pCursor = pFlac->dr->currentPCMFrame;
62230
62231 return MA_SUCCESS;
62232 }
62233 #else
62234 {
62235 /* flac is disabled. Should never hit this since initialization would have failed. */
62236 MA_ASSERT(MA_FALSE);
62237 return MA_NOT_IMPLEMENTED;
62238 }
62239 #endif
62240 }
62241
62242 MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength)
62243 {
62244 if (pLength == NULL) {
62245 return MA_INVALID_ARGS;
62246 }
62247
62248 *pLength = 0; /* Safety. */
62249
62250 if (pFlac == NULL) {
62251 return MA_INVALID_ARGS;
62252 }
62253
62254 #if !defined(MA_NO_FLAC)
62255 {
62256 *pLength = pFlac->dr->totalPCMFrameCount;
62257
62258 return MA_SUCCESS;
62259 }
62260 #else
62261 {
62262 /* flac is disabled. Should never hit this since initialization would have failed. */
62263 MA_ASSERT(MA_FALSE);
62264 return MA_NOT_IMPLEMENTED;
62265 }
62266 #endif
62267 }
62268
62269
62270 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)
62271 {
62272 ma_result result;
62273 ma_flac* pFlac;
62274
62275 (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */
62276
62277 /* For now we're just allocating the decoder backend on the heap. */
62278 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62279 if (pFlac == NULL) {
62280 return MA_OUT_OF_MEMORY;
62281 }
62282
62283 result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac);
62284 if (result != MA_SUCCESS) {
62285 ma_free(pFlac, pAllocationCallbacks);
62286 return result;
62287 }
62288
62289 *ppBackend = pFlac;
62290
62291 return MA_SUCCESS;
62292 }
62293
62294 static 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)
62295 {
62296 ma_result result;
62297 ma_flac* pFlac;
62298
62299 (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. */
62300
62301 /* For now we're just allocating the decoder backend on the heap. */
62302 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62303 if (pFlac == NULL) {
62304 return MA_OUT_OF_MEMORY;
62305 }
62306
62307 result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac);
62308 if (result != MA_SUCCESS) {
62309 ma_free(pFlac, pAllocationCallbacks);
62310 return result;
62311 }
62312
62313 *ppBackend = pFlac;
62314
62315 return MA_SUCCESS;
62316 }
62317
62318 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)
62319 {
62320 ma_result result;
62321 ma_flac* pFlac;
62322
62323 (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. */
62324
62325 /* For now we're just allocating the decoder backend on the heap. */
62326 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62327 if (pFlac == NULL) {
62328 return MA_OUT_OF_MEMORY;
62329 }
62330
62331 result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac);
62332 if (result != MA_SUCCESS) {
62333 ma_free(pFlac, pAllocationCallbacks);
62334 return result;
62335 }
62336
62337 *ppBackend = pFlac;
62338
62339 return MA_SUCCESS;
62340 }
62341
62342 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)
62343 {
62344 ma_result result;
62345 ma_flac* pFlac;
62346
62347 (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. */
62348
62349 /* For now we're just allocating the decoder backend on the heap. */
62350 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
62351 if (pFlac == NULL) {
62352 return MA_OUT_OF_MEMORY;
62353 }
62354
62355 result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac);
62356 if (result != MA_SUCCESS) {
62357 ma_free(pFlac, pAllocationCallbacks);
62358 return result;
62359 }
62360
62361 *ppBackend = pFlac;
62362
62363 return MA_SUCCESS;
62364 }
62365
62366 static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
62367 {
62368 ma_flac* pFlac = (ma_flac*)pBackend;
62369
62370 (void)pUserData;
62371
62372 ma_flac_uninit(pFlac, pAllocationCallbacks);
62373 ma_free(pFlac, pAllocationCallbacks);
62374 }
62375
62376 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac =
62377 {
62378 ma_decoding_backend_init__flac,
62379 ma_decoding_backend_init_file__flac,
62380 ma_decoding_backend_init_file_w__flac,
62381 ma_decoding_backend_init_memory__flac,
62382 ma_decoding_backend_uninit__flac
62383 };
62384
62385 static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62386 {
62387 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder);
62388 }
62389 #endif /* dr_flac_h */
62390
62391 /* MP3 */
62392 #ifdef dr_mp3_h
62393 #define MA_HAS_MP3
62394
62395 typedef struct
62396 {
62397 ma_data_source_base ds;
62398 ma_read_proc onRead;
62399 ma_seek_proc onSeek;
62400 ma_tell_proc onTell;
62401 void* pReadSeekTellUserData;
62402 ma_format format; /* Can be f32 or s16. */
62403 #if !defined(MA_NO_MP3)
62404 drmp3 dr;
62405 drmp3_uint32 seekPointCount;
62406 drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */
62407 #endif
62408 } ma_mp3;
62409
62410 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);
62411 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);
62412 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);
62413 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);
62414 MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks);
62415 MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
62416 MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex);
62417 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);
62418 MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor);
62419 MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength);
62420
62421
62422 static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
62423 {
62424 return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead);
62425 }
62426
62427 static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
62428 {
62429 return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex);
62430 }
62431
62432 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)
62433 {
62434 return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
62435 }
62436
62437 static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
62438 {
62439 return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor);
62440 }
62441
62442 static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
62443 {
62444 return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength);
62445 }
62446
62447 static ma_data_source_vtable g_ma_mp3_ds_vtable =
62448 {
62449 ma_mp3_ds_read,
62450 ma_mp3_ds_seek,
62451 ma_mp3_ds_get_data_format,
62452 ma_mp3_ds_get_cursor,
62453 ma_mp3_ds_get_length,
62454 NULL, /* onSetLooping */
62455 0
62456 };
62457
62458
62459 #if !defined(MA_NO_MP3)
62460 static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks)
62461 {
62462 drmp3_allocation_callbacks callbacks;
62463
62464 if (pAllocationCallbacks != NULL) {
62465 callbacks.onMalloc = pAllocationCallbacks->onMalloc;
62466 callbacks.onRealloc = pAllocationCallbacks->onRealloc;
62467 callbacks.onFree = pAllocationCallbacks->onFree;
62468 callbacks.pUserData = pAllocationCallbacks->pUserData;
62469 } else {
62470 callbacks.onMalloc = ma__malloc_default;
62471 callbacks.onRealloc = ma__realloc_default;
62472 callbacks.onFree = ma__free_default;
62473 callbacks.pUserData = NULL;
62474 }
62475
62476 return callbacks;
62477 }
62478
62479 static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
62480 {
62481 ma_mp3* pMP3 = (ma_mp3*)pUserData;
62482 ma_result result;
62483 size_t bytesRead;
62484
62485 MA_ASSERT(pMP3 != NULL);
62486
62487 result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
62488 (void)result;
62489
62490 return bytesRead;
62491 }
62492
62493 static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin)
62494 {
62495 ma_mp3* pMP3 = (ma_mp3*)pUserData;
62496 ma_result result;
62497 ma_seek_origin maSeekOrigin;
62498
62499 MA_ASSERT(pMP3 != NULL);
62500
62501 maSeekOrigin = ma_seek_origin_start;
62502 if (origin == drmp3_seek_origin_current) {
62503 maSeekOrigin = ma_seek_origin_current;
62504 }
62505
62506 result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin);
62507 if (result != MA_SUCCESS) {
62508 return MA_FALSE;
62509 }
62510
62511 return MA_TRUE;
62512 }
62513 #endif
62514
62515 static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3)
62516 {
62517 ma_result result;
62518 ma_data_source_config dataSourceConfig;
62519
62520 if (pMP3 == NULL) {
62521 return MA_INVALID_ARGS;
62522 }
62523
62524 MA_ZERO_OBJECT(pMP3);
62525 pMP3->format = ma_format_f32; /* f32 by default. */
62526
62527 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
62528 pMP3->format = pConfig->preferredFormat;
62529 } else {
62530 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
62531 }
62532
62533 dataSourceConfig = ma_data_source_config_init();
62534 dataSourceConfig.vtable = &g_ma_mp3_ds_vtable;
62535
62536 result = ma_data_source_init(&dataSourceConfig, &pMP3->ds);
62537 if (result != MA_SUCCESS) {
62538 return result; /* Failed to initialize the base data source. */
62539 }
62540
62541 return MA_SUCCESS;
62542 }
62543
62544 static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
62545 {
62546 drmp3_bool32 mp3Result;
62547 drmp3_uint32 seekPointCount = 0;
62548 drmp3_seek_point* pSeekPoints = NULL;
62549
62550 MA_ASSERT(pMP3 != NULL);
62551 MA_ASSERT(pConfig != NULL);
62552
62553 seekPointCount = pConfig->seekPointCount;
62554 if (seekPointCount > 0) {
62555 pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks);
62556 if (pSeekPoints == NULL) {
62557 return MA_OUT_OF_MEMORY;
62558 }
62559 }
62560
62561 mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints);
62562 if (mp3Result != MA_TRUE) {
62563 ma_free(pSeekPoints, pAllocationCallbacks);
62564 return MA_ERROR;
62565 }
62566
62567 mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints);
62568 if (mp3Result != MA_TRUE) {
62569 ma_free(pSeekPoints, pAllocationCallbacks);
62570 return MA_ERROR;
62571 }
62572
62573 pMP3->seekPointCount = seekPointCount;
62574 pMP3->pSeekPoints = pSeekPoints;
62575
62576 return MA_SUCCESS;
62577 }
62578
62579 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)
62580 {
62581 ma_result result;
62582
62583 result = ma_mp3_init_internal(pConfig, pMP3);
62584 if (result != MA_SUCCESS) {
62585 return result;
62586 }
62587
62588 if (onRead == NULL || onSeek == NULL) {
62589 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
62590 }
62591
62592 pMP3->onRead = onRead;
62593 pMP3->onSeek = onSeek;
62594 pMP3->onTell = onTell;
62595 pMP3->pReadSeekTellUserData = pReadSeekTellUserData;
62596
62597 #if !defined(MA_NO_MP3)
62598 {
62599 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
62600 drmp3_bool32 mp3Result;
62601
62602 mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks);
62603 if (mp3Result != MA_TRUE) {
62604 return MA_INVALID_FILE;
62605 }
62606
62607 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
62608
62609 return MA_SUCCESS;
62610 }
62611 #else
62612 {
62613 /* mp3 is disabled. */
62614 (void)pAllocationCallbacks;
62615 return MA_NOT_IMPLEMENTED;
62616 }
62617 #endif
62618 }
62619
62620 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)
62621 {
62622 ma_result result;
62623
62624 result = ma_mp3_init_internal(pConfig, pMP3);
62625 if (result != MA_SUCCESS) {
62626 return result;
62627 }
62628
62629 #if !defined(MA_NO_MP3)
62630 {
62631 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
62632 drmp3_bool32 mp3Result;
62633
62634 mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks);
62635 if (mp3Result != MA_TRUE) {
62636 return MA_INVALID_FILE;
62637 }
62638
62639 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
62640
62641 return MA_SUCCESS;
62642 }
62643 #else
62644 {
62645 /* mp3 is disabled. */
62646 (void)pFilePath;
62647 (void)pAllocationCallbacks;
62648 return MA_NOT_IMPLEMENTED;
62649 }
62650 #endif
62651 }
62652
62653 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)
62654 {
62655 ma_result result;
62656
62657 result = ma_mp3_init_internal(pConfig, pMP3);
62658 if (result != MA_SUCCESS) {
62659 return result;
62660 }
62661
62662 #if !defined(MA_NO_MP3)
62663 {
62664 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
62665 drmp3_bool32 mp3Result;
62666
62667 mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks);
62668 if (mp3Result != MA_TRUE) {
62669 return MA_INVALID_FILE;
62670 }
62671
62672 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
62673
62674 return MA_SUCCESS;
62675 }
62676 #else
62677 {
62678 /* mp3 is disabled. */
62679 (void)pFilePath;
62680 (void)pAllocationCallbacks;
62681 return MA_NOT_IMPLEMENTED;
62682 }
62683 #endif
62684 }
62685
62686 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)
62687 {
62688 ma_result result;
62689
62690 result = ma_mp3_init_internal(pConfig, pMP3);
62691 if (result != MA_SUCCESS) {
62692 return result;
62693 }
62694
62695 #if !defined(MA_NO_MP3)
62696 {
62697 drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks);
62698 drmp3_bool32 mp3Result;
62699
62700 mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks);
62701 if (mp3Result != MA_TRUE) {
62702 return MA_INVALID_FILE;
62703 }
62704
62705 ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
62706
62707 return MA_SUCCESS;
62708 }
62709 #else
62710 {
62711 /* mp3 is disabled. */
62712 (void)pData;
62713 (void)dataSize;
62714 (void)pAllocationCallbacks;
62715 return MA_NOT_IMPLEMENTED;
62716 }
62717 #endif
62718 }
62719
62720 MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks)
62721 {
62722 if (pMP3 == NULL) {
62723 return;
62724 }
62725
62726 #if !defined(MA_NO_MP3)
62727 {
62728 drmp3_uninit(&pMP3->dr);
62729 }
62730 #else
62731 {
62732 /* mp3 is disabled. Should never hit this since initialization would have failed. */
62733 MA_ASSERT(MA_FALSE);
62734 }
62735 #endif
62736
62737 /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */
62738 ma_free(pMP3->pSeekPoints, pAllocationCallbacks);
62739
62740 ma_data_source_uninit(&pMP3->ds);
62741 }
62742
62743 MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
62744 {
62745 if (pFramesRead != NULL) {
62746 *pFramesRead = 0;
62747 }
62748
62749 if (frameCount == 0) {
62750 return MA_INVALID_ARGS;
62751 }
62752
62753 if (pMP3 == NULL) {
62754 return MA_INVALID_ARGS;
62755 }
62756
62757 #if !defined(MA_NO_MP3)
62758 {
62759 /* We always use floating point format. */
62760 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
62761 ma_uint64 totalFramesRead = 0;
62762 ma_format format;
62763
62764 ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0);
62765
62766 switch (format)
62767 {
62768 case ma_format_f32:
62769 {
62770 totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut);
62771 } break;
62772
62773 case ma_format_s16:
62774 {
62775 totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut);
62776 } break;
62777
62778 case ma_format_u8:
62779 case ma_format_s24:
62780 case ma_format_s32:
62781 case ma_format_unknown:
62782 default:
62783 {
62784 return MA_INVALID_OPERATION;
62785 };
62786 }
62787
62788 /* In the future we'll update dr_mp3 to return MA_AT_END for us. */
62789 if (totalFramesRead == 0) {
62790 result = MA_AT_END;
62791 }
62792
62793 if (pFramesRead != NULL) {
62794 *pFramesRead = totalFramesRead;
62795 }
62796
62797 return result;
62798 }
62799 #else
62800 {
62801 /* mp3 is disabled. Should never hit this since initialization would have failed. */
62802 MA_ASSERT(MA_FALSE);
62803
62804 (void)pFramesOut;
62805 (void)frameCount;
62806 (void)pFramesRead;
62807
62808 return MA_NOT_IMPLEMENTED;
62809 }
62810 #endif
62811 }
62812
62813 MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex)
62814 {
62815 if (pMP3 == NULL) {
62816 return MA_INVALID_ARGS;
62817 }
62818
62819 #if !defined(MA_NO_MP3)
62820 {
62821 drmp3_bool32 mp3Result;
62822
62823 mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex);
62824 if (mp3Result != DRMP3_TRUE) {
62825 return MA_ERROR;
62826 }
62827
62828 return MA_SUCCESS;
62829 }
62830 #else
62831 {
62832 /* mp3 is disabled. Should never hit this since initialization would have failed. */
62833 MA_ASSERT(MA_FALSE);
62834
62835 (void)frameIndex;
62836
62837 return MA_NOT_IMPLEMENTED;
62838 }
62839 #endif
62840 }
62841
62842 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)
62843 {
62844 /* Defaults for safety. */
62845 if (pFormat != NULL) {
62846 *pFormat = ma_format_unknown;
62847 }
62848 if (pChannels != NULL) {
62849 *pChannels = 0;
62850 }
62851 if (pSampleRate != NULL) {
62852 *pSampleRate = 0;
62853 }
62854 if (pChannelMap != NULL) {
62855 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
62856 }
62857
62858 if (pMP3 == NULL) {
62859 return MA_INVALID_OPERATION;
62860 }
62861
62862 if (pFormat != NULL) {
62863 *pFormat = pMP3->format;
62864 }
62865
62866 #if !defined(MA_NO_MP3)
62867 {
62868 if (pChannels != NULL) {
62869 *pChannels = pMP3->dr.channels;
62870 }
62871
62872 if (pSampleRate != NULL) {
62873 *pSampleRate = pMP3->dr.sampleRate;
62874 }
62875
62876 if (pChannelMap != NULL) {
62877 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels);
62878 }
62879
62880 return MA_SUCCESS;
62881 }
62882 #else
62883 {
62884 /* mp3 is disabled. Should never hit this since initialization would have failed. */
62885 MA_ASSERT(MA_FALSE);
62886 return MA_NOT_IMPLEMENTED;
62887 }
62888 #endif
62889 }
62890
62891 MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor)
62892 {
62893 if (pCursor == NULL) {
62894 return MA_INVALID_ARGS;
62895 }
62896
62897 *pCursor = 0; /* Safety. */
62898
62899 if (pMP3 == NULL) {
62900 return MA_INVALID_ARGS;
62901 }
62902
62903 #if !defined(MA_NO_MP3)
62904 {
62905 *pCursor = pMP3->dr.currentPCMFrame;
62906
62907 return MA_SUCCESS;
62908 }
62909 #else
62910 {
62911 /* mp3 is disabled. Should never hit this since initialization would have failed. */
62912 MA_ASSERT(MA_FALSE);
62913 return MA_NOT_IMPLEMENTED;
62914 }
62915 #endif
62916 }
62917
62918 MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength)
62919 {
62920 if (pLength == NULL) {
62921 return MA_INVALID_ARGS;
62922 }
62923
62924 *pLength = 0; /* Safety. */
62925
62926 if (pMP3 == NULL) {
62927 return MA_INVALID_ARGS;
62928 }
62929
62930 #if !defined(MA_NO_MP3)
62931 {
62932 *pLength = drmp3_get_pcm_frame_count(&pMP3->dr);
62933
62934 return MA_SUCCESS;
62935 }
62936 #else
62937 {
62938 /* mp3 is disabled. Should never hit this since initialization would have failed. */
62939 MA_ASSERT(MA_FALSE);
62940 return MA_NOT_IMPLEMENTED;
62941 }
62942 #endif
62943 }
62944
62945
62946 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)
62947 {
62948 ma_result result;
62949 ma_mp3* pMP3;
62950
62951 (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. */
62952
62953 /* For now we're just allocating the decoder backend on the heap. */
62954 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
62955 if (pMP3 == NULL) {
62956 return MA_OUT_OF_MEMORY;
62957 }
62958
62959 result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3);
62960 if (result != MA_SUCCESS) {
62961 ma_free(pMP3, pAllocationCallbacks);
62962 return result;
62963 }
62964
62965 *ppBackend = pMP3;
62966
62967 return MA_SUCCESS;
62968 }
62969
62970 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)
62971 {
62972 ma_result result;
62973 ma_mp3* pMP3;
62974
62975 (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. */
62976
62977 /* For now we're just allocating the decoder backend on the heap. */
62978 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
62979 if (pMP3 == NULL) {
62980 return MA_OUT_OF_MEMORY;
62981 }
62982
62983 result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3);
62984 if (result != MA_SUCCESS) {
62985 ma_free(pMP3, pAllocationCallbacks);
62986 return result;
62987 }
62988
62989 *ppBackend = pMP3;
62990
62991 return MA_SUCCESS;
62992 }
62993
62994 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)
62995 {
62996 ma_result result;
62997 ma_mp3* pMP3;
62998
62999 (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. */
63000
63001 /* For now we're just allocating the decoder backend on the heap. */
63002 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
63003 if (pMP3 == NULL) {
63004 return MA_OUT_OF_MEMORY;
63005 }
63006
63007 result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3);
63008 if (result != MA_SUCCESS) {
63009 ma_free(pMP3, pAllocationCallbacks);
63010 return result;
63011 }
63012
63013 *ppBackend = pMP3;
63014
63015 return MA_SUCCESS;
63016 }
63017
63018 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)
63019 {
63020 ma_result result;
63021 ma_mp3* pMP3;
63022
63023 (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. */
63024
63025 /* For now we're just allocating the decoder backend on the heap. */
63026 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
63027 if (pMP3 == NULL) {
63028 return MA_OUT_OF_MEMORY;
63029 }
63030
63031 result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3);
63032 if (result != MA_SUCCESS) {
63033 ma_free(pMP3, pAllocationCallbacks);
63034 return result;
63035 }
63036
63037 *ppBackend = pMP3;
63038
63039 return MA_SUCCESS;
63040 }
63041
63042 static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
63043 {
63044 ma_mp3* pMP3 = (ma_mp3*)pBackend;
63045
63046 (void)pUserData;
63047
63048 ma_mp3_uninit(pMP3, pAllocationCallbacks);
63049 ma_free(pMP3, pAllocationCallbacks);
63050 }
63051
63052 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 =
63053 {
63054 ma_decoding_backend_init__mp3,
63055 ma_decoding_backend_init_file__mp3,
63056 ma_decoding_backend_init_file_w__mp3,
63057 ma_decoding_backend_init_memory__mp3,
63058 ma_decoding_backend_uninit__mp3
63059 };
63060
63061 static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63062 {
63063 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder);
63064 }
63065 #endif /* dr_mp3_h */
63066
63067 /* Vorbis */
63068 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
63069 #define MA_HAS_VORBIS
63070
63071 /* The size in bytes of each chunk of data to read from the Vorbis stream. */
63072 #define MA_VORBIS_DATA_CHUNK_SIZE 4096
63073
63074 typedef struct
63075 {
63076 ma_data_source_base ds;
63077 ma_read_proc onRead;
63078 ma_seek_proc onSeek;
63079 ma_tell_proc onTell;
63080 void* pReadSeekTellUserData;
63081 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. */
63082 ma_format format; /* Only f32 is allowed with stb_vorbis. */
63083 ma_uint32 channels;
63084 ma_uint32 sampleRate;
63085 ma_uint64 cursor;
63086 #if !defined(MA_NO_VORBIS)
63087 stb_vorbis* stb;
63088 ma_bool32 usingPushMode;
63089 struct
63090 {
63091 ma_uint8* pData;
63092 size_t dataSize;
63093 size_t dataCapacity;
63094 size_t audioStartOffsetInBytes;
63095 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
63096 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
63097 float** ppPacketData;
63098 } push;
63099 #endif
63100 } ma_stbvorbis;
63101
63102 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);
63103 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);
63104 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);
63105 MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
63106 MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
63107 MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);
63108 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);
63109 MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);
63110 MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);
63111
63112
63113 static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63114 {
63115 return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
63116 }
63117
63118 static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
63119 {
63120 return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);
63121 }
63122
63123 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)
63124 {
63125 return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
63126 }
63127
63128 static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
63129 {
63130 return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);
63131 }
63132
63133 static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
63134 {
63135 return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);
63136 }
63137
63138 static ma_data_source_vtable g_ma_stbvorbis_ds_vtable =
63139 {
63140 ma_stbvorbis_ds_read,
63141 ma_stbvorbis_ds_seek,
63142 ma_stbvorbis_ds_get_data_format,
63143 ma_stbvorbis_ds_get_cursor,
63144 ma_stbvorbis_ds_get_length,
63145 NULL, /* onSetLooping */
63146 0
63147 };
63148
63149
63150 static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)
63151 {
63152 ma_result result;
63153 ma_data_source_config dataSourceConfig;
63154
63155 (void)pConfig;
63156
63157 if (pVorbis == NULL) {
63158 return MA_INVALID_ARGS;
63159 }
63160
63161 MA_ZERO_OBJECT(pVorbis);
63162 pVorbis->format = ma_format_f32; /* Only supporting f32. */
63163
63164 dataSourceConfig = ma_data_source_config_init();
63165 dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;
63166
63167 result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
63168 if (result != MA_SUCCESS) {
63169 return result; /* Failed to initialize the base data source. */
63170 }
63171
63172 return MA_SUCCESS;
63173 }
63174
63175 #if !defined(MA_NO_VORBIS)
63176 static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)
63177 {
63178 stb_vorbis_info info;
63179
63180 MA_ASSERT(pVorbis != NULL);
63181
63182 info = stb_vorbis_get_info(pVorbis->stb);
63183
63184 pVorbis->channels = info.channels;
63185 pVorbis->sampleRate = info.sample_rate;
63186
63187 return MA_SUCCESS;
63188 }
63189
63190 static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis)
63191 {
63192 ma_result result;
63193 stb_vorbis* stb;
63194 size_t dataSize = 0;
63195 size_t dataCapacity = 0;
63196 ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */
63197
63198 for (;;) {
63199 int vorbisError;
63200 int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */
63201 size_t bytesRead;
63202 ma_uint8* pNewData;
63203
63204 /* Allocate memory for the new chunk. */
63205 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
63206 pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks);
63207 if (pNewData == NULL) {
63208 ma_free(pData, &pVorbis->allocationCallbacks);
63209 return MA_OUT_OF_MEMORY;
63210 }
63211
63212 pData = pNewData;
63213
63214 /* Read in the next chunk. */
63215 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);
63216 dataSize += bytesRead;
63217
63218 if (result != MA_SUCCESS) {
63219 ma_free(pData, &pVorbis->allocationCallbacks);
63220 return result;
63221 }
63222
63223 /* We have a maximum of 31 bits with stb_vorbis. */
63224 if (dataSize > INT_MAX) {
63225 ma_free(pData, &pVorbis->allocationCallbacks);
63226 return MA_TOO_BIG;
63227 }
63228
63229 stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
63230 if (stb != NULL) {
63231 /*
63232 Successfully opened the Vorbis decoder. We might have some leftover unprocessed
63233 data so we'll need to move that down to the front.
63234 */
63235 dataSize -= (size_t)consumedDataSize; /* Consume the data. */
63236 MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);
63237
63238 /*
63239 We need to track the start point so we can seek back to the start of the audio
63240 data when seeking.
63241 */
63242 pVorbis->push.audioStartOffsetInBytes = consumedDataSize;
63243
63244 break;
63245 } else {
63246 /* Failed to open the decoder. */
63247 if (vorbisError == VORBIS_need_more_data) {
63248 continue;
63249 } else {
63250 ma_free(pData, &pVorbis->allocationCallbacks);
63251 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
63252 }
63253 }
63254 }
63255
63256 MA_ASSERT(stb != NULL);
63257 pVorbis->stb = stb;
63258 pVorbis->push.pData = pData;
63259 pVorbis->push.dataSize = dataSize;
63260 pVorbis->push.dataCapacity = dataCapacity;
63261
63262 return MA_SUCCESS;
63263 }
63264 #endif
63265
63266 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)
63267 {
63268 ma_result result;
63269
63270 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
63271 if (result != MA_SUCCESS) {
63272 return result;
63273 }
63274
63275 if (onRead == NULL || onSeek == NULL) {
63276 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
63277 }
63278
63279 pVorbis->onRead = onRead;
63280 pVorbis->onSeek = onSeek;
63281 pVorbis->onTell = onTell;
63282 pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
63283 ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);
63284
63285 #if !defined(MA_NO_VORBIS)
63286 {
63287 /*
63288 stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the
63289 pushing API. In order for us to be able to successfully initialize the decoder we need to
63290 supply it with enough data. We need to keep loading data until we have enough.
63291 */
63292 result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
63293 if (result != MA_SUCCESS) {
63294 return result;
63295 }
63296
63297 pVorbis->usingPushMode = MA_TRUE;
63298
63299 result = ma_stbvorbis_post_init(pVorbis);
63300 if (result != MA_SUCCESS) {
63301 stb_vorbis_close(pVorbis->stb);
63302 ma_free(pVorbis->push.pData, pAllocationCallbacks);
63303 return result;
63304 }
63305
63306 return MA_SUCCESS;
63307 }
63308 #else
63309 {
63310 /* vorbis is disabled. */
63311 (void)pAllocationCallbacks;
63312 return MA_NOT_IMPLEMENTED;
63313 }
63314 #endif
63315 }
63316
63317 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)
63318 {
63319 ma_result result;
63320
63321 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
63322 if (result != MA_SUCCESS) {
63323 return result;
63324 }
63325
63326 #if !defined(MA_NO_VORBIS)
63327 {
63328 (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */
63329
63330 /* We can use stb_vorbis' pull mode for file based streams. */
63331 pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);
63332 if (pVorbis->stb == NULL) {
63333 return MA_INVALID_FILE;
63334 }
63335
63336 pVorbis->usingPushMode = MA_FALSE;
63337
63338 result = ma_stbvorbis_post_init(pVorbis);
63339 if (result != MA_SUCCESS) {
63340 stb_vorbis_close(pVorbis->stb);
63341 return result;
63342 }
63343
63344 return MA_SUCCESS;
63345 }
63346 #else
63347 {
63348 /* vorbis is disabled. */
63349 (void)pFilePath;
63350 (void)pAllocationCallbacks;
63351 return MA_NOT_IMPLEMENTED;
63352 }
63353 #endif
63354 }
63355
63356 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)
63357 {
63358 ma_result result;
63359
63360 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
63361 if (result != MA_SUCCESS) {
63362 return result;
63363 }
63364
63365 #if !defined(MA_NO_VORBIS)
63366 {
63367 (void)pAllocationCallbacks;
63368
63369 /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
63370 if (dataSize > INT_MAX) {
63371 return MA_TOO_BIG;
63372 }
63373
63374 pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
63375 if (pVorbis->stb == NULL) {
63376 return MA_INVALID_FILE;
63377 }
63378
63379 pVorbis->usingPushMode = MA_FALSE;
63380
63381 result = ma_stbvorbis_post_init(pVorbis);
63382 if (result != MA_SUCCESS) {
63383 stb_vorbis_close(pVorbis->stb);
63384 return result;
63385 }
63386
63387 return MA_SUCCESS;
63388 }
63389 #else
63390 {
63391 /* vorbis is disabled. */
63392 (void)pData;
63393 (void)dataSize;
63394 (void)pAllocationCallbacks;
63395 return MA_NOT_IMPLEMENTED;
63396 }
63397 #endif
63398 }
63399
63400 MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
63401 {
63402 if (pVorbis == NULL) {
63403 return;
63404 }
63405
63406 #if !defined(MA_NO_VORBIS)
63407 {
63408 stb_vorbis_close(pVorbis->stb);
63409
63410 /* We'll have to clear some memory if we're using push mode. */
63411 if (pVorbis->usingPushMode) {
63412 ma_free(pVorbis->push.pData, pAllocationCallbacks);
63413 }
63414 }
63415 #else
63416 {
63417 /* vorbis is disabled. Should never hit this since initialization would have failed. */
63418 MA_ASSERT(MA_FALSE);
63419 }
63420 #endif
63421
63422 ma_data_source_uninit(&pVorbis->ds);
63423 }
63424
63425 MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63426 {
63427 if (pFramesRead != NULL) {
63428 *pFramesRead = 0;
63429 }
63430
63431 if (frameCount == 0) {
63432 return MA_INVALID_ARGS;
63433 }
63434
63435 if (pVorbis == NULL) {
63436 return MA_INVALID_ARGS;
63437 }
63438
63439 #if !defined(MA_NO_VORBIS)
63440 {
63441 /* We always use floating point format. */
63442 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
63443 ma_uint64 totalFramesRead = 0;
63444 ma_format format;
63445 ma_uint32 channels;
63446
63447 ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
63448
63449 if (format == ma_format_f32) {
63450 /* We read differently depending on whether or not we're using push mode. */
63451 if (pVorbis->usingPushMode) {
63452 /* Push mode. This is the complex case. */
63453 float* pFramesOutF32 = (float*)pFramesOut;
63454
63455 while (totalFramesRead < frameCount) {
63456 /* The first thing to do is read from any already-cached frames. */
63457 ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
63458
63459 /* The output pointer can be null in which case we just treate it as a seek. */
63460 if (pFramesOut != NULL) {
63461 ma_uint64 iFrame;
63462 for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
63463 ma_uint32 iChannel;
63464 for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {
63465 pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];
63466 }
63467
63468 pFramesOutF32 += pVorbis->channels;
63469 }
63470 }
63471
63472 /* Update pointers and counters. */
63473 pVorbis->push.framesConsumed += framesToReadFromCache;
63474 pVorbis->push.framesRemaining -= framesToReadFromCache;
63475 totalFramesRead += framesToReadFromCache;
63476
63477 /* Don't bother reading any more frames right now if we've just finished loading. */
63478 if (totalFramesRead == frameCount) {
63479 break;
63480 }
63481
63482 MA_ASSERT(pVorbis->push.framesRemaining == 0);
63483
63484 /* Getting here means we've run out of cached frames. We'll need to load some more. */
63485 for (;;) {
63486 int samplesRead = 0;
63487 int consumedDataSize;
63488
63489 /* We need to case dataSize to an int, so make sure we can do it safely. */
63490 if (pVorbis->push.dataSize > INT_MAX) {
63491 break; /* Too big. */
63492 }
63493
63494 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);
63495 if (consumedDataSize != 0) {
63496 /* Successfully decoded a Vorbis frame. Consume the data. */
63497 pVorbis->push.dataSize -= (size_t)consumedDataSize;
63498 MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);
63499
63500 pVorbis->push.framesConsumed = 0;
63501 pVorbis->push.framesRemaining = samplesRead;
63502
63503 break;
63504 } else {
63505 /* Not enough data. Read more. */
63506 size_t bytesRead;
63507
63508 /* Expand the data buffer if necessary. */
63509 if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {
63510 size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
63511 ma_uint8* pNewData;
63512
63513 pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);
63514 if (pNewData == NULL) {
63515 result = MA_OUT_OF_MEMORY;
63516 break;
63517 }
63518
63519 pVorbis->push.pData = pNewData;
63520 pVorbis->push.dataCapacity = newCap;
63521 }
63522
63523 /* We should have enough room to load some data. */
63524 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);
63525 pVorbis->push.dataSize += bytesRead;
63526
63527 if (result != MA_SUCCESS) {
63528 break; /* Failed to read any data. Get out. */
63529 }
63530 }
63531 }
63532
63533 /* 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). */
63534 if (result != MA_SUCCESS) {
63535 break;
63536 }
63537 }
63538 } else {
63539 /* 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. */
63540 while (totalFramesRead < frameCount) {
63541 ma_uint64 framesRemaining = (frameCount - totalFramesRead);
63542 int framesRead;
63543
63544 if (framesRemaining > INT_MAX) {
63545 framesRemaining = INT_MAX;
63546 }
63547
63548 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. */
63549 totalFramesRead += framesRead;
63550
63551 if (framesRead < (int)framesRemaining) {
63552 break; /* Nothing left to read. Get out. */
63553 }
63554 }
63555 }
63556 } else {
63557 result = MA_INVALID_ARGS;
63558 }
63559
63560 pVorbis->cursor += totalFramesRead;
63561
63562 if (totalFramesRead == 0) {
63563 result = MA_AT_END;
63564 }
63565
63566 if (pFramesRead != NULL) {
63567 *pFramesRead = totalFramesRead;
63568 }
63569
63570 if (result == MA_SUCCESS && totalFramesRead == 0) {
63571 result = MA_AT_END;
63572 }
63573
63574 return result;
63575 }
63576 #else
63577 {
63578 /* vorbis is disabled. Should never hit this since initialization would have failed. */
63579 MA_ASSERT(MA_FALSE);
63580
63581 (void)pFramesOut;
63582 (void)frameCount;
63583 (void)pFramesRead;
63584
63585 return MA_NOT_IMPLEMENTED;
63586 }
63587 #endif
63588 }
63589
63590 MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)
63591 {
63592 if (pVorbis == NULL) {
63593 return MA_INVALID_ARGS;
63594 }
63595
63596 #if !defined(MA_NO_VORBIS)
63597 {
63598 /* Different seeking methods depending on whether or not we're using push mode. */
63599 if (pVorbis->usingPushMode) {
63600 /* Push mode. This is the complex case. */
63601 ma_result result;
63602 float buffer[4096];
63603
63604 /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */
63605 if (frameIndex < pVorbis->cursor) {
63606 if (frameIndex > 0x7FFFFFFF) {
63607 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
63608 }
63609
63610 /*
63611 This is wildly inefficient due to me having trouble getting sample exact seeking working
63612 robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work
63613 perfectly is to reinitialize the decoder. Note that we only enter this path when seeking
63614 backwards. This will hopefully be removed once we get our own Vorbis decoder implemented.
63615 */
63616 stb_vorbis_close(pVorbis->stb);
63617 ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks);
63618
63619 MA_ZERO_OBJECT(&pVorbis->push);
63620
63621 /* Seek to the start of the file. */
63622 result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);
63623 if (result != MA_SUCCESS) {
63624 return result;
63625 }
63626
63627 result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
63628 if (result != MA_SUCCESS) {
63629 return result;
63630 }
63631
63632 /* At this point we should be sitting on the first frame. */
63633 pVorbis->cursor = 0;
63634 }
63635
63636 /* We're just brute-forcing this for now. */
63637 while (pVorbis->cursor < frameIndex) {
63638 ma_uint64 framesRead;
63639 ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;
63640 if (framesToRead > (frameIndex - pVorbis->cursor)) {
63641 framesToRead = (frameIndex - pVorbis->cursor);
63642 }
63643
63644 result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);
63645 pVorbis->cursor += framesRead;
63646
63647 if (result != MA_SUCCESS) {
63648 return result;
63649 }
63650 }
63651 } else {
63652 /* Pull mode. This is the simple case. */
63653 int vorbisResult;
63654
63655 if (frameIndex > UINT_MAX) {
63656 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
63657 }
63658
63659 vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */
63660 if (vorbisResult == 0) {
63661 return MA_ERROR; /* See failed. */
63662 }
63663
63664 pVorbis->cursor = frameIndex;
63665 }
63666
63667 return MA_SUCCESS;
63668 }
63669 #else
63670 {
63671 /* vorbis is disabled. Should never hit this since initialization would have failed. */
63672 MA_ASSERT(MA_FALSE);
63673
63674 (void)frameIndex;
63675
63676 return MA_NOT_IMPLEMENTED;
63677 }
63678 #endif
63679 }
63680
63681 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)
63682 {
63683 /* Defaults for safety. */
63684 if (pFormat != NULL) {
63685 *pFormat = ma_format_unknown;
63686 }
63687 if (pChannels != NULL) {
63688 *pChannels = 0;
63689 }
63690 if (pSampleRate != NULL) {
63691 *pSampleRate = 0;
63692 }
63693 if (pChannelMap != NULL) {
63694 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
63695 }
63696
63697 if (pVorbis == NULL) {
63698 return MA_INVALID_OPERATION;
63699 }
63700
63701 if (pFormat != NULL) {
63702 *pFormat = pVorbis->format;
63703 }
63704
63705 #if !defined(MA_NO_VORBIS)
63706 {
63707 if (pChannels != NULL) {
63708 *pChannels = pVorbis->channels;
63709 }
63710
63711 if (pSampleRate != NULL) {
63712 *pSampleRate = pVorbis->sampleRate;
63713 }
63714
63715 if (pChannelMap != NULL) {
63716 ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels);
63717 }
63718
63719 return MA_SUCCESS;
63720 }
63721 #else
63722 {
63723 /* vorbis is disabled. Should never hit this since initialization would have failed. */
63724 MA_ASSERT(MA_FALSE);
63725 return MA_NOT_IMPLEMENTED;
63726 }
63727 #endif
63728 }
63729
63730 MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)
63731 {
63732 if (pCursor == NULL) {
63733 return MA_INVALID_ARGS;
63734 }
63735
63736 *pCursor = 0; /* Safety. */
63737
63738 if (pVorbis == NULL) {
63739 return MA_INVALID_ARGS;
63740 }
63741
63742 #if !defined(MA_NO_VORBIS)
63743 {
63744 *pCursor = pVorbis->cursor;
63745
63746 return MA_SUCCESS;
63747 }
63748 #else
63749 {
63750 /* vorbis is disabled. Should never hit this since initialization would have failed. */
63751 MA_ASSERT(MA_FALSE);
63752 return MA_NOT_IMPLEMENTED;
63753 }
63754 #endif
63755 }
63756
63757 MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)
63758 {
63759 if (pLength == NULL) {
63760 return MA_INVALID_ARGS;
63761 }
63762
63763 *pLength = 0; /* Safety. */
63764
63765 if (pVorbis == NULL) {
63766 return MA_INVALID_ARGS;
63767 }
63768
63769 #if !defined(MA_NO_VORBIS)
63770 {
63771 if (pVorbis->usingPushMode) {
63772 *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */
63773 } else {
63774 *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);
63775 }
63776
63777 return MA_SUCCESS;
63778 }
63779 #else
63780 {
63781 /* vorbis is disabled. Should never hit this since initialization would have failed. */
63782 MA_ASSERT(MA_FALSE);
63783 return MA_NOT_IMPLEMENTED;
63784 }
63785 #endif
63786 }
63787
63788
63789 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)
63790 {
63791 ma_result result;
63792 ma_stbvorbis* pVorbis;
63793
63794 (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. */
63795
63796 /* For now we're just allocating the decoder backend on the heap. */
63797 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
63798 if (pVorbis == NULL) {
63799 return MA_OUT_OF_MEMORY;
63800 }
63801
63802 result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
63803 if (result != MA_SUCCESS) {
63804 ma_free(pVorbis, pAllocationCallbacks);
63805 return result;
63806 }
63807
63808 *ppBackend = pVorbis;
63809
63810 return MA_SUCCESS;
63811 }
63812
63813 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)
63814 {
63815 ma_result result;
63816 ma_stbvorbis* pVorbis;
63817
63818 (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. */
63819
63820 /* For now we're just allocating the decoder backend on the heap. */
63821 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
63822 if (pVorbis == NULL) {
63823 return MA_OUT_OF_MEMORY;
63824 }
63825
63826 result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
63827 if (result != MA_SUCCESS) {
63828 ma_free(pVorbis, pAllocationCallbacks);
63829 return result;
63830 }
63831
63832 *ppBackend = pVorbis;
63833
63834 return MA_SUCCESS;
63835 }
63836
63837 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)
63838 {
63839 ma_result result;
63840 ma_stbvorbis* pVorbis;
63841
63842 (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. */
63843
63844 /* For now we're just allocating the decoder backend on the heap. */
63845 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
63846 if (pVorbis == NULL) {
63847 return MA_OUT_OF_MEMORY;
63848 }
63849
63850 result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);
63851 if (result != MA_SUCCESS) {
63852 ma_free(pVorbis, pAllocationCallbacks);
63853 return result;
63854 }
63855
63856 *ppBackend = pVorbis;
63857
63858 return MA_SUCCESS;
63859 }
63860
63861 static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
63862 {
63863 ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
63864
63865 (void)pUserData;
63866
63867 ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);
63868 ma_free(pVorbis, pAllocationCallbacks);
63869 }
63870
63871 static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =
63872 {
63873 ma_decoding_backend_init__stbvorbis,
63874 ma_decoding_backend_init_file__stbvorbis,
63875 NULL, /* onInitFileW() */
63876 ma_decoding_backend_init_memory__stbvorbis,
63877 ma_decoding_backend_uninit__stbvorbis
63878 };
63879
63880 static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63881 {
63882 return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);
63883 }
63884 #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
63885
63886
63887
63888 static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63889 {
63890 MA_ASSERT(pDecoder != NULL);
63891
63892 if (pConfig != NULL) {
63893 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
63894 } else {
63895 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
63896 return MA_SUCCESS;
63897 }
63898 }
63899
63900 static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63901 {
63902 return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead);
63903 }
63904
63905 static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
63906 {
63907 return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
63908 }
63909
63910 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)
63911 {
63912 return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
63913 }
63914
63915 static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
63916 {
63917 return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor);
63918 }
63919
63920 static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
63921 {
63922 return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength);
63923 }
63924
63925 static ma_data_source_vtable g_ma_decoder_data_source_vtable =
63926 {
63927 ma_decoder__data_source_on_read,
63928 ma_decoder__data_source_on_seek,
63929 ma_decoder__data_source_on_get_data_format,
63930 ma_decoder__data_source_on_get_cursor,
63931 ma_decoder__data_source_on_get_length,
63932 NULL, /* onSetLooping */
63933 0
63934 };
63935
63936 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)
63937 {
63938 ma_result result;
63939 ma_data_source_config dataSourceConfig;
63940
63941 MA_ASSERT(pConfig != NULL);
63942
63943 if (pDecoder == NULL) {
63944 return MA_INVALID_ARGS;
63945 }
63946
63947 MA_ZERO_OBJECT(pDecoder);
63948
63949 if (onRead == NULL || onSeek == NULL) {
63950 return MA_INVALID_ARGS;
63951 }
63952
63953 dataSourceConfig = ma_data_source_config_init();
63954 dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable;
63955
63956 result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds);
63957 if (result != MA_SUCCESS) {
63958 return result;
63959 }
63960
63961 pDecoder->onRead = onRead;
63962 pDecoder->onSeek = onSeek;
63963 pDecoder->onTell = onTell;
63964 pDecoder->pUserData = pUserData;
63965
63966 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
63967 if (result != MA_SUCCESS) {
63968 ma_data_source_uninit(&pDecoder->ds);
63969 return result;
63970 }
63971
63972 return MA_SUCCESS;
63973 }
63974
63975 static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63976 {
63977 ma_result result;
63978
63979 result = ma_decoder__init_data_converter(pDecoder, pConfig);
63980
63981 /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */
63982 if (result != MA_SUCCESS) {
63983 ma_decoder_uninit(pDecoder);
63984 return result;
63985 }
63986
63987 return result;
63988 }
63989
63990
63991 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)
63992 {
63993 ma_result result = MA_NO_BACKEND;
63994
63995 MA_ASSERT(pConfig != NULL);
63996 MA_ASSERT(pDecoder != NULL);
63997
63998 /* Silence some warnings in the case that we don't have any decoder backends enabled. */
63999 (void)onRead;
64000 (void)onSeek;
64001 (void)pUserData;
64002
64003
64004 /* If we've specified a specific encoding type, try that first. */
64005 if (pConfig->encodingFormat != ma_encoding_format_unknown) {
64006 #ifdef MA_HAS_WAV
64007 if (pConfig->encodingFormat == ma_encoding_format_wav) {
64008 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
64009 }
64010 #endif
64011 #ifdef MA_HAS_FLAC
64012 if (pConfig->encodingFormat == ma_encoding_format_flac) {
64013 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
64014 }
64015 #endif
64016 #ifdef MA_HAS_MP3
64017 if (pConfig->encodingFormat == ma_encoding_format_mp3) {
64018 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
64019 }
64020 #endif
64021 #ifdef MA_HAS_VORBIS
64022 if (pConfig->encodingFormat == ma_encoding_format_vorbis) {
64023 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
64024 }
64025 #endif
64026
64027 /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */
64028 if (result != MA_SUCCESS) {
64029 onSeek(pDecoder, 0, ma_seek_origin_start);
64030 }
64031 }
64032
64033 if (result != MA_SUCCESS) {
64034 /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */
64035
64036 /*
64037 We use trial and error to open a decoder. We prioritize custom decoders so that if they
64038 implement the same encoding format they take priority over the built-in decoders.
64039 */
64040 if (result != MA_SUCCESS) {
64041 result = ma_decoder_init_custom__internal(pConfig, pDecoder);
64042 if (result != MA_SUCCESS) {
64043 onSeek(pDecoder, 0, ma_seek_origin_start);
64044 }
64045 }
64046
64047 /*
64048 If we get to this point and we still haven't found a decoder, and the caller has requested a
64049 specific encoding format, there's no hope for it. Abort.
64050 */
64051 if (pConfig->encodingFormat != ma_encoding_format_unknown) {
64052 return MA_NO_BACKEND;
64053 }
64054
64055 #ifdef MA_HAS_WAV
64056 if (result != MA_SUCCESS) {
64057 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
64058 if (result != MA_SUCCESS) {
64059 onSeek(pDecoder, 0, ma_seek_origin_start);
64060 }
64061 }
64062 #endif
64063 #ifdef MA_HAS_FLAC
64064 if (result != MA_SUCCESS) {
64065 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
64066 if (result != MA_SUCCESS) {
64067 onSeek(pDecoder, 0, ma_seek_origin_start);
64068 }
64069 }
64070 #endif
64071 #ifdef MA_HAS_MP3
64072 if (result != MA_SUCCESS) {
64073 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
64074 if (result != MA_SUCCESS) {
64075 onSeek(pDecoder, 0, ma_seek_origin_start);
64076 }
64077 }
64078 #endif
64079 #ifdef MA_HAS_VORBIS
64080 if (result != MA_SUCCESS) {
64081 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
64082 if (result != MA_SUCCESS) {
64083 onSeek(pDecoder, 0, ma_seek_origin_start);
64084 }
64085 }
64086 #endif
64087 }
64088
64089 if (result != MA_SUCCESS) {
64090 return result;
64091 }
64092
64093 return ma_decoder__postinit(pConfig, pDecoder);
64094 }
64095
64096 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)
64097 {
64098 ma_decoder_config config;
64099 ma_result result;
64100
64101 config = ma_decoder_config_init_copy(pConfig);
64102
64103 result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
64104 if (result != MA_SUCCESS) {
64105 return result;
64106 }
64107
64108 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
64109 }
64110
64111
64112 static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
64113 {
64114 size_t bytesRemaining;
64115
64116 MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);
64117
64118 if (pBytesRead != NULL) {
64119 *pBytesRead = 0;
64120 }
64121
64122 bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;
64123 if (bytesToRead > bytesRemaining) {
64124 bytesToRead = bytesRemaining;
64125 }
64126
64127 if (bytesRemaining == 0) {
64128 return MA_AT_END;
64129 }
64130
64131 if (bytesToRead > 0) {
64132 MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);
64133 pDecoder->data.memory.currentReadPos += bytesToRead;
64134 }
64135
64136 if (pBytesRead != NULL) {
64137 *pBytesRead = bytesToRead;
64138 }
64139
64140 return MA_SUCCESS;
64141 }
64142
64143 static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
64144 {
64145 if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) {
64146 return MA_BAD_SEEK;
64147 }
64148
64149 if (origin == ma_seek_origin_current) {
64150 if (byteOffset > 0) {
64151 if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {
64152 byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */
64153 }
64154
64155 pDecoder->data.memory.currentReadPos += (size_t)byteOffset;
64156 } else {
64157 if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {
64158 byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */
64159 }
64160
64161 pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset;
64162 }
64163 } else {
64164 if (origin == ma_seek_origin_end) {
64165 if (byteOffset < 0) {
64166 byteOffset = -byteOffset;
64167 }
64168
64169 if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {
64170 pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */
64171 } else {
64172 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset;
64173 }
64174 } else {
64175 if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {
64176 pDecoder->data.memory.currentReadPos = (size_t)byteOffset;
64177 } else {
64178 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */
64179 }
64180 }
64181 }
64182
64183 return MA_SUCCESS;
64184 }
64185
64186 static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)
64187 {
64188 MA_ASSERT(pDecoder != NULL);
64189 MA_ASSERT(pCursor != NULL);
64190
64191 *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;
64192
64193 return MA_SUCCESS;
64194 }
64195
64196 static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64197 {
64198 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);
64199 if (result != MA_SUCCESS) {
64200 return result;
64201 }
64202
64203 if (pData == NULL || dataSize == 0) {
64204 return MA_INVALID_ARGS;
64205 }
64206
64207 pDecoder->data.memory.pData = (const ma_uint8*)pData;
64208 pDecoder->data.memory.dataSize = dataSize;
64209 pDecoder->data.memory.currentReadPos = 0;
64210
64211 (void)pConfig;
64212 return MA_SUCCESS;
64213 }
64214
64215 MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64216 {
64217 ma_decoder_config config;
64218 ma_result result;
64219
64220 config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
64221
64222 result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
64223 if (result != MA_SUCCESS) {
64224 return result;
64225 }
64226
64227 return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
64228 }
64229
64230
64231 #if defined(MA_HAS_WAV) || \
64232 defined(MA_HAS_MP3) || \
64233 defined(MA_HAS_FLAC) || \
64234 defined(MA_HAS_VORBIS) || \
64235 defined(MA_HAS_OPUS)
64236 #define MA_HAS_PATH_API
64237 #endif
64238
64239 #if defined(MA_HAS_PATH_API)
64240 static const char* ma_path_file_name(const char* path)
64241 {
64242 const char* fileName;
64243
64244 if (path == NULL) {
64245 return NULL;
64246 }
64247
64248 fileName = path;
64249
64250 /* We just loop through the path until we find the last slash. */
64251 while (path[0] != '\0') {
64252 if (path[0] == '/' || path[0] == '\\') {
64253 fileName = path;
64254 }
64255
64256 path += 1;
64257 }
64258
64259 /* At this point the file name is sitting on a slash, so just move forward. */
64260 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
64261 fileName += 1;
64262 }
64263
64264 return fileName;
64265 }
64266
64267 static const wchar_t* ma_path_file_name_w(const wchar_t* path)
64268 {
64269 const wchar_t* fileName;
64270
64271 if (path == NULL) {
64272 return NULL;
64273 }
64274
64275 fileName = path;
64276
64277 /* We just loop through the path until we find the last slash. */
64278 while (path[0] != '\0') {
64279 if (path[0] == '/' || path[0] == '\\') {
64280 fileName = path;
64281 }
64282
64283 path += 1;
64284 }
64285
64286 /* At this point the file name is sitting on a slash, so just move forward. */
64287 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
64288 fileName += 1;
64289 }
64290
64291 return fileName;
64292 }
64293
64294
64295 static const char* ma_path_extension(const char* path)
64296 {
64297 const char* extension;
64298 const char* lastOccurance;
64299
64300 if (path == NULL) {
64301 path = "";
64302 }
64303
64304 extension = ma_path_file_name(path);
64305 lastOccurance = NULL;
64306
64307 /* Just find the last '.' and return. */
64308 while (extension[0] != '\0') {
64309 if (extension[0] == '.') {
64310 extension += 1;
64311 lastOccurance = extension;
64312 }
64313
64314 extension += 1;
64315 }
64316
64317 return (lastOccurance != NULL) ? lastOccurance : extension;
64318 }
64319
64320 static const wchar_t* ma_path_extension_w(const wchar_t* path)
64321 {
64322 const wchar_t* extension;
64323 const wchar_t* lastOccurance;
64324
64325 if (path == NULL) {
64326 path = L"";
64327 }
64328
64329 extension = ma_path_file_name_w(path);
64330 lastOccurance = NULL;
64331
64332 /* Just find the last '.' and return. */
64333 while (extension[0] != '\0') {
64334 if (extension[0] == '.') {
64335 extension += 1;
64336 lastOccurance = extension;
64337 }
64338
64339 extension += 1;
64340 }
64341
64342 return (lastOccurance != NULL) ? lastOccurance : extension;
64343 }
64344
64345
64346 static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
64347 {
64348 const char* ext1;
64349 const char* ext2;
64350
64351 if (path == NULL || extension == NULL) {
64352 return MA_FALSE;
64353 }
64354
64355 ext1 = extension;
64356 ext2 = ma_path_extension(path);
64357
64358 #if defined(_MSC_VER) || defined(__DMC__)
64359 return _stricmp(ext1, ext2) == 0;
64360 #else
64361 return strcasecmp(ext1, ext2) == 0;
64362 #endif
64363 }
64364
64365 static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
64366 {
64367 const wchar_t* ext1;
64368 const wchar_t* ext2;
64369
64370 if (path == NULL || extension == NULL) {
64371 return MA_FALSE;
64372 }
64373
64374 ext1 = extension;
64375 ext2 = ma_path_extension_w(path);
64376
64377 #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)
64378 return _wcsicmp(ext1, ext2) == 0;
64379 #else
64380 /*
64381 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
64382 isn't the most efficient way to do it, but it should work OK.
64383 */
64384 {
64385 char ext1MB[4096];
64386 char ext2MB[4096];
64387 const wchar_t* pext1 = ext1;
64388 const wchar_t* pext2 = ext2;
64389 mbstate_t mbs1;
64390 mbstate_t mbs2;
64391
64392 MA_ZERO_OBJECT(&mbs1);
64393 MA_ZERO_OBJECT(&mbs2);
64394
64395 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
64396 return MA_FALSE;
64397 }
64398 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
64399 return MA_FALSE;
64400 }
64401
64402 return strcasecmp(ext1MB, ext2MB) == 0;
64403 }
64404 #endif
64405 }
64406 #endif /* MA_HAS_PATH_API */
64407
64408
64409
64410 static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
64411 {
64412 MA_ASSERT(pDecoder != NULL);
64413 MA_ASSERT(pBufferOut != NULL);
64414
64415 return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead);
64416 }
64417
64418 static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)
64419 {
64420 MA_ASSERT(pDecoder != NULL);
64421
64422 return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);
64423 }
64424
64425 static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)
64426 {
64427 MA_ASSERT(pDecoder != NULL);
64428
64429 return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);
64430 }
64431
64432 static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64433 {
64434 ma_result result;
64435 ma_vfs_file file;
64436
64437 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
64438 if (result != MA_SUCCESS) {
64439 return result;
64440 }
64441
64442 if (pFilePath == NULL || pFilePath[0] == '\0') {
64443 return MA_INVALID_ARGS;
64444 }
64445
64446 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
64447 if (result != MA_SUCCESS) {
64448 return result;
64449 }
64450
64451 pDecoder->data.vfs.pVFS = pVFS;
64452 pDecoder->data.vfs.file = file;
64453
64454 return MA_SUCCESS;
64455 }
64456
64457 MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64458 {
64459 ma_result result;
64460 ma_decoder_config config;
64461
64462 config = ma_decoder_config_init_copy(pConfig);
64463 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
64464 if (result != MA_SUCCESS) {
64465 return result;
64466 }
64467
64468 result = MA_NO_BACKEND;
64469
64470 if (config.encodingFormat != ma_encoding_format_unknown) {
64471 #ifdef MA_HAS_WAV
64472 if (config.encodingFormat == ma_encoding_format_wav) {
64473 result = ma_decoder_init_wav__internal(&config, pDecoder);
64474 }
64475 #endif
64476 #ifdef MA_HAS_FLAC
64477 if (config.encodingFormat == ma_encoding_format_flac) {
64478 result = ma_decoder_init_flac__internal(&config, pDecoder);
64479 }
64480 #endif
64481 #ifdef MA_HAS_MP3
64482 if (config.encodingFormat == ma_encoding_format_mp3) {
64483 result = ma_decoder_init_mp3__internal(&config, pDecoder);
64484 }
64485 #endif
64486 #ifdef MA_HAS_VORBIS
64487 if (config.encodingFormat == ma_encoding_format_vorbis) {
64488 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
64489 }
64490 #endif
64491
64492 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
64493 if (result != MA_SUCCESS) {
64494 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64495 }
64496 }
64497
64498 if (result != MA_SUCCESS) {
64499 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
64500
64501 /*
64502 We use trial and error to open a decoder. We prioritize custom decoders so that if they
64503 implement the same encoding format they take priority over the built-in decoders.
64504 */
64505 if (result != MA_SUCCESS) {
64506 result = ma_decoder_init_custom__internal(&config, pDecoder);
64507 if (result != MA_SUCCESS) {
64508 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64509 }
64510 }
64511
64512 /*
64513 If we get to this point and we still haven't found a decoder, and the caller has requested a
64514 specific encoding format, there's no hope for it. Abort.
64515 */
64516 if (config.encodingFormat != ma_encoding_format_unknown) {
64517 return MA_NO_BACKEND;
64518 }
64519
64520 #ifdef MA_HAS_WAV
64521 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
64522 result = ma_decoder_init_wav__internal(&config, pDecoder);
64523 if (result != MA_SUCCESS) {
64524 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64525 }
64526 }
64527 #endif
64528 #ifdef MA_HAS_FLAC
64529 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
64530 result = ma_decoder_init_flac__internal(&config, pDecoder);
64531 if (result != MA_SUCCESS) {
64532 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64533 }
64534 }
64535 #endif
64536 #ifdef MA_HAS_MP3
64537 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
64538 result = ma_decoder_init_mp3__internal(&config, pDecoder);
64539 if (result != MA_SUCCESS) {
64540 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64541 }
64542 }
64543 #endif
64544 }
64545
64546 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
64547 if (result != MA_SUCCESS) {
64548 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
64549 } else {
64550 result = ma_decoder__postinit(&config, pDecoder);
64551 }
64552
64553 if (result != MA_SUCCESS) {
64554 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. */
64555 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
64556 }
64557
64558 return result;
64559 }
64560
64561 return MA_SUCCESS;
64562 }
64563
64564
64565 static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64566 {
64567 ma_result result;
64568 ma_vfs_file file;
64569
64570 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
64571 if (result != MA_SUCCESS) {
64572 return result;
64573 }
64574
64575 if (pFilePath == NULL || pFilePath[0] == '\0') {
64576 return MA_INVALID_ARGS;
64577 }
64578
64579 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
64580 if (result != MA_SUCCESS) {
64581 return result;
64582 }
64583
64584 pDecoder->data.vfs.pVFS = pVFS;
64585 pDecoder->data.vfs.file = file;
64586
64587 return MA_SUCCESS;
64588 }
64589
64590 MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64591 {
64592 ma_result result;
64593 ma_decoder_config config;
64594
64595 config = ma_decoder_config_init_copy(pConfig);
64596 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
64597 if (result != MA_SUCCESS) {
64598 return result;
64599 }
64600
64601 result = MA_NO_BACKEND;
64602
64603 if (config.encodingFormat != ma_encoding_format_unknown) {
64604 #ifdef MA_HAS_WAV
64605 if (config.encodingFormat == ma_encoding_format_wav) {
64606 result = ma_decoder_init_wav__internal(&config, pDecoder);
64607 }
64608 #endif
64609 #ifdef MA_HAS_FLAC
64610 if (config.encodingFormat == ma_encoding_format_flac) {
64611 result = ma_decoder_init_flac__internal(&config, pDecoder);
64612 }
64613 #endif
64614 #ifdef MA_HAS_MP3
64615 if (config.encodingFormat == ma_encoding_format_mp3) {
64616 result = ma_decoder_init_mp3__internal(&config, pDecoder);
64617 }
64618 #endif
64619 #ifdef MA_HAS_VORBIS
64620 if (config.encodingFormat == ma_encoding_format_vorbis) {
64621 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
64622 }
64623 #endif
64624
64625 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
64626 if (result != MA_SUCCESS) {
64627 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64628 }
64629 }
64630
64631 if (result != MA_SUCCESS) {
64632 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
64633
64634 /*
64635 We use trial and error to open a decoder. We prioritize custom decoders so that if they
64636 implement the same encoding format they take priority over the built-in decoders.
64637 */
64638 if (result != MA_SUCCESS) {
64639 result = ma_decoder_init_custom__internal(&config, pDecoder);
64640 if (result != MA_SUCCESS) {
64641 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64642 }
64643 }
64644
64645 /*
64646 If we get to this point and we still haven't found a decoder, and the caller has requested a
64647 specific encoding format, there's no hope for it. Abort.
64648 */
64649 if (config.encodingFormat != ma_encoding_format_unknown) {
64650 return MA_NO_BACKEND;
64651 }
64652
64653 #ifdef MA_HAS_WAV
64654 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
64655 result = ma_decoder_init_wav__internal(&config, pDecoder);
64656 if (result != MA_SUCCESS) {
64657 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64658 }
64659 }
64660 #endif
64661 #ifdef MA_HAS_FLAC
64662 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
64663 result = ma_decoder_init_flac__internal(&config, pDecoder);
64664 if (result != MA_SUCCESS) {
64665 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64666 }
64667 }
64668 #endif
64669 #ifdef MA_HAS_MP3
64670 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
64671 result = ma_decoder_init_mp3__internal(&config, pDecoder);
64672 if (result != MA_SUCCESS) {
64673 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
64674 }
64675 }
64676 #endif
64677 }
64678
64679 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
64680 if (result != MA_SUCCESS) {
64681 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
64682 } else {
64683 result = ma_decoder__postinit(&config, pDecoder);
64684 }
64685
64686 if (result != MA_SUCCESS) {
64687 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
64688 return result;
64689 }
64690
64691 return MA_SUCCESS;
64692 }
64693
64694 MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64695 {
64696 return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);
64697 }
64698
64699 MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64700 {
64701 return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
64702 }
64703
64704 MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
64705 {
64706 if (pDecoder == NULL) {
64707 return MA_INVALID_ARGS;
64708 }
64709
64710 if (pDecoder->pBackend != NULL) {
64711 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
64712 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
64713 }
64714 }
64715
64716 if (pDecoder->onRead == ma_decoder__on_read_vfs) {
64717 ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
64718 pDecoder->data.vfs.file = NULL;
64719 }
64720
64721 ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
64722 ma_data_source_uninit(&pDecoder->ds);
64723
64724 if (pDecoder->pInputCache != NULL) {
64725 ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks);
64726 }
64727
64728 return MA_SUCCESS;
64729 }
64730
64731 MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
64732 {
64733 ma_result result = MA_SUCCESS;
64734 ma_uint64 totalFramesReadOut;
64735 void* pRunningFramesOut;
64736
64737 if (pFramesRead != NULL) {
64738 *pFramesRead = 0; /* Safety. */
64739 }
64740
64741 if (frameCount == 0) {
64742 return MA_INVALID_ARGS;
64743 }
64744
64745 if (pDecoder == NULL) {
64746 return MA_INVALID_ARGS;
64747 }
64748
64749 if (pDecoder->pBackend == NULL) {
64750 return MA_INVALID_OPERATION;
64751 }
64752
64753 /* Fast path. */
64754 if (pDecoder->converter.isPassthrough) {
64755 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut);
64756 } else {
64757 /*
64758 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
64759 need to run through each sample because we need to ensure it's internal cache is updated.
64760 */
64761 if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
64762 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);
64763 } else {
64764 /* Slow path. Need to run everything through the data converter. */
64765 ma_format internalFormat;
64766 ma_uint32 internalChannels;
64767
64768 totalFramesReadOut = 0;
64769 pRunningFramesOut = pFramesOut;
64770
64771 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0);
64772 if (result != MA_SUCCESS) {
64773 return result; /* Failed to retrieve the internal format and channel count. */
64774 }
64775
64776 /*
64777 We run a different path depending on whether or not we are using a heap-allocated
64778 intermediary buffer or not. If the data converter does not support the calculation of
64779 the required number of input frames, we'll use the heap-allocated path. Otherwise we'll
64780 use the stack-allocated path.
64781 */
64782 if (pDecoder->pInputCache != NULL) {
64783 /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */
64784 while (totalFramesReadOut < frameCount) {
64785 ma_uint64 framesToReadThisIterationIn;
64786 ma_uint64 framesToReadThisIterationOut;
64787
64788 /* If there's any data available in the cache, that needs to get processed first. */
64789 if (pDecoder->inputCacheRemaining > 0) {
64790 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
64791 framesToReadThisIterationIn = framesToReadThisIterationOut;
64792 if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) {
64793 framesToReadThisIterationIn = pDecoder->inputCacheRemaining;
64794 }
64795
64796 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);
64797 if (result != MA_SUCCESS) {
64798 break;
64799 }
64800
64801 pDecoder->inputCacheConsumed += framesToReadThisIterationIn;
64802 pDecoder->inputCacheRemaining -= framesToReadThisIterationIn;
64803
64804 totalFramesReadOut += framesToReadThisIterationOut;
64805
64806 if (pRunningFramesOut != NULL) {
64807 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
64808 }
64809
64810 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
64811 break; /* We're done. */
64812 }
64813 }
64814
64815 /* Getting here means there's no data in the cache and we need to fill it up from the data source. */
64816 if (pDecoder->inputCacheRemaining == 0) {
64817 pDecoder->inputCacheConsumed = 0;
64818
64819 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining);
64820 if (result != MA_SUCCESS) {
64821 break;
64822 }
64823 }
64824 }
64825 } else {
64826 /* We have a way of determining the required number of input frames so just use the stack. */
64827 while (totalFramesReadOut < frameCount) {
64828 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
64829 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels);
64830 ma_uint64 framesToReadThisIterationIn;
64831 ma_uint64 framesReadThisIterationIn;
64832 ma_uint64 framesToReadThisIterationOut;
64833 ma_uint64 framesReadThisIterationOut;
64834 ma_uint64 requiredInputFrameCount;
64835
64836 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
64837 framesToReadThisIterationIn = framesToReadThisIterationOut;
64838 if (framesToReadThisIterationIn > intermediaryBufferCap) {
64839 framesToReadThisIterationIn = intermediaryBufferCap;
64840 }
64841
64842 ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount);
64843 if (framesToReadThisIterationIn > requiredInputFrameCount) {
64844 framesToReadThisIterationIn = requiredInputFrameCount;
64845 }
64846
64847 if (requiredInputFrameCount > 0) {
64848 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);
64849 } else {
64850 framesReadThisIterationIn = 0;
64851 }
64852
64853 /*
64854 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
64855 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
64856 */
64857 framesReadThisIterationOut = framesToReadThisIterationOut;
64858 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
64859 if (result != MA_SUCCESS) {
64860 break;
64861 }
64862
64863 totalFramesReadOut += framesReadThisIterationOut;
64864
64865 if (pRunningFramesOut != NULL) {
64866 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
64867 }
64868
64869 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
64870 break; /* We're done. */
64871 }
64872 }
64873 }
64874 }
64875 }
64876
64877 pDecoder->readPointerInPCMFrames += totalFramesReadOut;
64878
64879 if (pFramesRead != NULL) {
64880 *pFramesRead = totalFramesReadOut;
64881 }
64882
64883 if (result == MA_SUCCESS && totalFramesReadOut == 0) {
64884 result = MA_AT_END;
64885 }
64886
64887 return result;
64888 }
64889
64890 MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)
64891 {
64892 if (pDecoder == NULL) {
64893 return MA_INVALID_ARGS;
64894 }
64895
64896 if (pDecoder->pBackend != NULL) {
64897 ma_result result;
64898 ma_uint64 internalFrameIndex;
64899 ma_uint32 internalSampleRate;
64900 ma_uint64 currentFrameIndex;
64901
64902 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
64903 if (result != MA_SUCCESS) {
64904 return result; /* Failed to retrieve the internal sample rate. */
64905 }
64906
64907 if (internalSampleRate == pDecoder->outputSampleRate) {
64908 internalFrameIndex = frameIndex;
64909 } else {
64910 internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);
64911 }
64912
64913 /* Only seek if we're requesting a different frame to what we're currently sitting on. */
64914 ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, &currentFrameIndex);
64915 if (currentFrameIndex != internalFrameIndex) {
64916 result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
64917 if (result == MA_SUCCESS) {
64918 pDecoder->readPointerInPCMFrames = frameIndex;
64919 }
64920
64921 /* Reset the data converter so that any cached data in the resampler is cleared. */
64922 ma_data_converter_reset(&pDecoder->converter);
64923 }
64924
64925 return result;
64926 }
64927
64928 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
64929 return MA_INVALID_ARGS;
64930 }
64931
64932 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)
64933 {
64934 if (pDecoder == NULL) {
64935 return MA_INVALID_ARGS;
64936 }
64937
64938 if (pFormat != NULL) {
64939 *pFormat = pDecoder->outputFormat;
64940 }
64941
64942 if (pChannels != NULL) {
64943 *pChannels = pDecoder->outputChannels;
64944 }
64945
64946 if (pSampleRate != NULL) {
64947 *pSampleRate = pDecoder->outputSampleRate;
64948 }
64949
64950 if (pChannelMap != NULL) {
64951 ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap);
64952 }
64953
64954 return MA_SUCCESS;
64955 }
64956
64957 MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor)
64958 {
64959 if (pCursor == NULL) {
64960 return MA_INVALID_ARGS;
64961 }
64962
64963 *pCursor = 0;
64964
64965 if (pDecoder == NULL) {
64966 return MA_INVALID_ARGS;
64967 }
64968
64969 *pCursor = pDecoder->readPointerInPCMFrames;
64970
64971 return MA_SUCCESS;
64972 }
64973
64974 MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength)
64975 {
64976 if (pLength == NULL) {
64977 return MA_INVALID_ARGS;
64978 }
64979
64980 *pLength = 0;
64981
64982 if (pDecoder == NULL) {
64983 return MA_INVALID_ARGS;
64984 }
64985
64986 if (pDecoder->pBackend != NULL) {
64987 ma_result result;
64988 ma_uint64 internalLengthInPCMFrames;
64989 ma_uint32 internalSampleRate;
64990
64991 result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames);
64992 if (result != MA_SUCCESS) {
64993 return result; /* Failed to retrieve the internal length. */
64994 }
64995
64996 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
64997 if (result != MA_SUCCESS) {
64998 return result; /* Failed to retrieve the internal sample rate. */
64999 }
65000
65001 if (internalSampleRate == pDecoder->outputSampleRate) {
65002 *pLength = internalLengthInPCMFrames;
65003 } else {
65004 *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames);
65005 }
65006
65007 return MA_SUCCESS;
65008 } else {
65009 return MA_NO_BACKEND;
65010 }
65011 }
65012
65013 MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)
65014 {
65015 ma_result result;
65016 ma_uint64 totalFrameCount;
65017
65018 if (pAvailableFrames == NULL) {
65019 return MA_INVALID_ARGS;
65020 }
65021
65022 *pAvailableFrames = 0;
65023
65024 if (pDecoder == NULL) {
65025 return MA_INVALID_ARGS;
65026 }
65027
65028 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
65029 if (result != MA_SUCCESS) {
65030 return result;
65031 }
65032
65033 if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
65034 *pAvailableFrames = 0;
65035 } else {
65036 *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
65037 }
65038
65039 return MA_SUCCESS;
65040 }
65041
65042
65043 static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65044 {
65045 ma_result result;
65046 ma_uint64 totalFrameCount;
65047 ma_uint64 bpf;
65048 ma_uint64 dataCapInFrames;
65049 void* pPCMFramesOut;
65050
65051 MA_ASSERT(pDecoder != NULL);
65052
65053 totalFrameCount = 0;
65054 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
65055
65056 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
65057 dataCapInFrames = 0;
65058 pPCMFramesOut = NULL;
65059 for (;;) {
65060 ma_uint64 frameCountToTryReading;
65061 ma_uint64 framesJustRead;
65062
65063 /* Make room if there's not enough. */
65064 if (totalFrameCount == dataCapInFrames) {
65065 void* pNewPCMFramesOut;
65066 ma_uint64 newDataCapInFrames = dataCapInFrames*2;
65067 if (newDataCapInFrames == 0) {
65068 newDataCapInFrames = 4096;
65069 }
65070
65071 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
65072 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
65073 return MA_TOO_BIG;
65074 }
65075
65076 pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
65077 if (pNewPCMFramesOut == NULL) {
65078 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
65079 return MA_OUT_OF_MEMORY;
65080 }
65081
65082 dataCapInFrames = newDataCapInFrames;
65083 pPCMFramesOut = pNewPCMFramesOut;
65084 }
65085
65086 frameCountToTryReading = dataCapInFrames - totalFrameCount;
65087 MA_ASSERT(frameCountToTryReading > 0);
65088
65089 result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead);
65090 totalFrameCount += framesJustRead;
65091
65092 if (result != MA_SUCCESS) {
65093 break;
65094 }
65095
65096 if (framesJustRead < frameCountToTryReading) {
65097 break;
65098 }
65099 }
65100
65101
65102 if (pConfigOut != NULL) {
65103 pConfigOut->format = pDecoder->outputFormat;
65104 pConfigOut->channels = pDecoder->outputChannels;
65105 pConfigOut->sampleRate = pDecoder->outputSampleRate;
65106 }
65107
65108 if (ppPCMFramesOut != NULL) {
65109 *ppPCMFramesOut = pPCMFramesOut;
65110 } else {
65111 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
65112 }
65113
65114 if (pFrameCountOut != NULL) {
65115 *pFrameCountOut = totalFrameCount;
65116 }
65117
65118 ma_decoder_uninit(pDecoder);
65119 return MA_SUCCESS;
65120 }
65121
65122 MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65123 {
65124 ma_result result;
65125 ma_decoder_config config;
65126 ma_decoder decoder;
65127
65128 if (pFrameCountOut != NULL) {
65129 *pFrameCountOut = 0;
65130 }
65131 if (ppPCMFramesOut != NULL) {
65132 *ppPCMFramesOut = NULL;
65133 }
65134
65135 config = ma_decoder_config_init_copy(pConfig);
65136
65137 result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);
65138 if (result != MA_SUCCESS) {
65139 return result;
65140 }
65141
65142 result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
65143
65144 return result;
65145 }
65146
65147 MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65148 {
65149 return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);
65150 }
65151
65152 MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
65153 {
65154 ma_decoder_config config;
65155 ma_decoder decoder;
65156 ma_result result;
65157
65158 if (pFrameCountOut != NULL) {
65159 *pFrameCountOut = 0;
65160 }
65161 if (ppPCMFramesOut != NULL) {
65162 *ppPCMFramesOut = NULL;
65163 }
65164
65165 if (pData == NULL || dataSize == 0) {
65166 return MA_INVALID_ARGS;
65167 }
65168
65169 config = ma_decoder_config_init_copy(pConfig);
65170
65171 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
65172 if (result != MA_SUCCESS) {
65173 return result;
65174 }
65175
65176 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
65177 }
65178 #endif /* MA_NO_DECODING */
65179
65180
65181 #ifndef MA_NO_ENCODING
65182
65183 #if defined(MA_HAS_WAV)
65184 static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
65185 {
65186 ma_encoder* pEncoder = (ma_encoder*)pUserData;
65187 size_t bytesWritten = 0;
65188
65189 MA_ASSERT(pEncoder != NULL);
65190
65191 pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten);
65192 return bytesWritten;
65193 }
65194
65195 static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin)
65196 {
65197 ma_encoder* pEncoder = (ma_encoder*)pUserData;
65198 ma_result result;
65199
65200 MA_ASSERT(pEncoder != NULL);
65201
65202 result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
65203 if (result != MA_SUCCESS) {
65204 return DRWAV_FALSE;
65205 } else {
65206 return DRWAV_TRUE;
65207 }
65208 }
65209
65210 static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
65211 {
65212 drwav_data_format wavFormat;
65213 drwav_allocation_callbacks allocationCallbacks;
65214 drwav* pWav;
65215
65216 MA_ASSERT(pEncoder != NULL);
65217
65218 pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
65219 if (pWav == NULL) {
65220 return MA_OUT_OF_MEMORY;
65221 }
65222
65223 wavFormat.container = drwav_container_riff;
65224 wavFormat.channels = pEncoder->config.channels;
65225 wavFormat.sampleRate = pEncoder->config.sampleRate;
65226 wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
65227 if (pEncoder->config.format == ma_format_f32) {
65228 wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT;
65229 } else {
65230 wavFormat.format = DR_WAVE_FORMAT_PCM;
65231 }
65232
65233 allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
65234 allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
65235 allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
65236 allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
65237
65238 if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
65239 return MA_ERROR;
65240 }
65241
65242 pEncoder->pInternalEncoder = pWav;
65243
65244 return MA_SUCCESS;
65245 }
65246
65247 static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
65248 {
65249 drwav* pWav;
65250
65251 MA_ASSERT(pEncoder != NULL);
65252
65253 pWav = (drwav*)pEncoder->pInternalEncoder;
65254 MA_ASSERT(pWav != NULL);
65255
65256 drwav_uninit(pWav);
65257 ma_free(pWav, &pEncoder->config.allocationCallbacks);
65258 }
65259
65260 static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
65261 {
65262 drwav* pWav;
65263 ma_uint64 framesWritten;
65264
65265 MA_ASSERT(pEncoder != NULL);
65266
65267 pWav = (drwav*)pEncoder->pInternalEncoder;
65268 MA_ASSERT(pWav != NULL);
65269
65270 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn);
65271
65272 if (pFramesWritten != NULL) {
65273 *pFramesWritten = framesWritten;
65274 }
65275
65276 return MA_SUCCESS;
65277 }
65278 #endif
65279
65280 MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
65281 {
65282 ma_encoder_config config;
65283
65284 MA_ZERO_OBJECT(&config);
65285 config.encodingFormat = encodingFormat;
65286 config.format = format;
65287 config.channels = channels;
65288 config.sampleRate = sampleRate;
65289
65290 return config;
65291 }
65292
65293 MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65294 {
65295 ma_result result;
65296
65297 if (pEncoder == NULL) {
65298 return MA_INVALID_ARGS;
65299 }
65300
65301 MA_ZERO_OBJECT(pEncoder);
65302
65303 if (pConfig == NULL) {
65304 return MA_INVALID_ARGS;
65305 }
65306
65307 if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
65308 return MA_INVALID_ARGS;
65309 }
65310
65311 pEncoder->config = *pConfig;
65312
65313 result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
65314 if (result != MA_SUCCESS) {
65315 return result;
65316 }
65317
65318 return MA_SUCCESS;
65319 }
65320
65321 MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
65322 {
65323 ma_result result = MA_SUCCESS;
65324
65325 /* This assumes ma_encoder_preinit() has been called prior. */
65326 MA_ASSERT(pEncoder != NULL);
65327
65328 if (onWrite == NULL || onSeek == NULL) {
65329 return MA_INVALID_ARGS;
65330 }
65331
65332 pEncoder->onWrite = onWrite;
65333 pEncoder->onSeek = onSeek;
65334 pEncoder->pUserData = pUserData;
65335
65336 switch (pEncoder->config.encodingFormat)
65337 {
65338 case ma_encoding_format_wav:
65339 {
65340 #if defined(MA_HAS_WAV)
65341 pEncoder->onInit = ma_encoder__on_init_wav;
65342 pEncoder->onUninit = ma_encoder__on_uninit_wav;
65343 pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
65344 #else
65345 result = MA_NO_BACKEND;
65346 #endif
65347 } break;
65348
65349 default:
65350 {
65351 result = MA_INVALID_ARGS;
65352 } break;
65353 }
65354
65355 /* Getting here means we should have our backend callbacks set up. */
65356 if (result == MA_SUCCESS) {
65357 result = pEncoder->onInit(pEncoder);
65358 }
65359
65360 return result;
65361 }
65362
65363 static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten)
65364 {
65365 return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten);
65366 }
65367
65368 static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin)
65369 {
65370 return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin);
65371 }
65372
65373 MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65374 {
65375 ma_result result;
65376 ma_vfs_file file;
65377
65378 result = ma_encoder_preinit(pConfig, pEncoder);
65379 if (result != MA_SUCCESS) {
65380 return result;
65381 }
65382
65383 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
65384 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
65385 if (result != MA_SUCCESS) {
65386 return result;
65387 }
65388
65389 pEncoder->data.vfs.pVFS = pVFS;
65390 pEncoder->data.vfs.file = file;
65391
65392 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
65393 if (result != MA_SUCCESS) {
65394 ma_vfs_or_default_close(pVFS, file);
65395 return result;
65396 }
65397
65398 return MA_SUCCESS;
65399 }
65400
65401 MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65402 {
65403 ma_result result;
65404 ma_vfs_file file;
65405
65406 result = ma_encoder_preinit(pConfig, pEncoder);
65407 if (result != MA_SUCCESS) {
65408 return result;
65409 }
65410
65411 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
65412 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
65413 if (result != MA_SUCCESS) {
65414 return result;
65415 }
65416
65417 pEncoder->data.vfs.pVFS = pVFS;
65418 pEncoder->data.vfs.file = file;
65419
65420 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
65421 if (result != MA_SUCCESS) {
65422 ma_vfs_or_default_close(pVFS, file);
65423 return result;
65424 }
65425
65426 return MA_SUCCESS;
65427 }
65428
65429 MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65430 {
65431 return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder);
65432 }
65433
65434 MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
65435 {
65436 return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder);
65437 }
65438
65439 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)
65440 {
65441 ma_result result;
65442
65443 result = ma_encoder_preinit(pConfig, pEncoder);
65444 if (result != MA_SUCCESS) {
65445 return result;
65446 }
65447
65448 return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
65449 }
65450
65451
65452 MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
65453 {
65454 if (pEncoder == NULL) {
65455 return;
65456 }
65457
65458 if (pEncoder->onUninit) {
65459 pEncoder->onUninit(pEncoder);
65460 }
65461
65462 /* If we have a file handle, close it. */
65463 if (pEncoder->onWrite == ma_encoder__on_write_vfs) {
65464 ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file);
65465 pEncoder->data.vfs.file = NULL;
65466 }
65467 }
65468
65469
65470 MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
65471 {
65472 if (pFramesWritten != NULL) {
65473 *pFramesWritten = 0;
65474 }
65475
65476 if (pEncoder == NULL || pFramesIn == NULL) {
65477 return MA_INVALID_ARGS;
65478 }
65479
65480 return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten);
65481 }
65482 #endif /* MA_NO_ENCODING */
65483
65484
65485
65486 /**************************************************************************************************************************************************************
65487
65488 Generation
65489
65490 **************************************************************************************************************************************************************/
65491 #ifndef MA_NO_GENERATION
65492 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)
65493 {
65494 ma_waveform_config config;
65495
65496 MA_ZERO_OBJECT(&config);
65497 config.format = format;
65498 config.channels = channels;
65499 config.sampleRate = sampleRate;
65500 config.type = type;
65501 config.amplitude = amplitude;
65502 config.frequency = frequency;
65503
65504 return config;
65505 }
65506
65507 static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65508 {
65509 return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead);
65510 }
65511
65512 static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
65513 {
65514 return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
65515 }
65516
65517 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)
65518 {
65519 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
65520
65521 *pFormat = pWaveform->config.format;
65522 *pChannels = pWaveform->config.channels;
65523 *pSampleRate = pWaveform->config.sampleRate;
65524 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels);
65525
65526 return MA_SUCCESS;
65527 }
65528
65529 static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
65530 {
65531 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
65532
65533 *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
65534
65535 return MA_SUCCESS;
65536 }
65537
65538 static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)
65539 {
65540 return (1.0 / (sampleRate / frequency));
65541 }
65542
65543 static void ma_waveform__update_advance(ma_waveform* pWaveform)
65544 {
65545 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
65546 }
65547
65548 static ma_data_source_vtable g_ma_waveform_data_source_vtable =
65549 {
65550 ma_waveform__data_source_on_read,
65551 ma_waveform__data_source_on_seek,
65552 ma_waveform__data_source_on_get_data_format,
65553 ma_waveform__data_source_on_get_cursor,
65554 NULL, /* onGetLength. There's no notion of a length in waveforms. */
65555 NULL, /* onSetLooping */
65556 0
65557 };
65558
65559 MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
65560 {
65561 ma_result result;
65562 ma_data_source_config dataSourceConfig;
65563
65564 if (pWaveform == NULL) {
65565 return MA_INVALID_ARGS;
65566 }
65567
65568 MA_ZERO_OBJECT(pWaveform);
65569
65570 dataSourceConfig = ma_data_source_config_init();
65571 dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable;
65572
65573 result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds);
65574 if (result != MA_SUCCESS) {
65575 return result;
65576 }
65577
65578 pWaveform->config = *pConfig;
65579 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
65580 pWaveform->time = 0;
65581
65582 return MA_SUCCESS;
65583 }
65584
65585 MA_API void ma_waveform_uninit(ma_waveform* pWaveform)
65586 {
65587 if (pWaveform == NULL) {
65588 return;
65589 }
65590
65591 ma_data_source_uninit(&pWaveform->ds);
65592 }
65593
65594 MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
65595 {
65596 if (pWaveform == NULL) {
65597 return MA_INVALID_ARGS;
65598 }
65599
65600 pWaveform->config.amplitude = amplitude;
65601 return MA_SUCCESS;
65602 }
65603
65604 MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
65605 {
65606 if (pWaveform == NULL) {
65607 return MA_INVALID_ARGS;
65608 }
65609
65610 pWaveform->config.frequency = frequency;
65611 ma_waveform__update_advance(pWaveform);
65612
65613 return MA_SUCCESS;
65614 }
65615
65616 MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type)
65617 {
65618 if (pWaveform == NULL) {
65619 return MA_INVALID_ARGS;
65620 }
65621
65622 pWaveform->config.type = type;
65623 return MA_SUCCESS;
65624 }
65625
65626 MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
65627 {
65628 if (pWaveform == NULL) {
65629 return MA_INVALID_ARGS;
65630 }
65631
65632 pWaveform->config.sampleRate = sampleRate;
65633 ma_waveform__update_advance(pWaveform);
65634
65635 return MA_SUCCESS;
65636 }
65637
65638 static float ma_waveform_sine_f32(double time, double amplitude)
65639 {
65640 return (float)(ma_sind(MA_TAU_D * time) * amplitude);
65641 }
65642
65643 static ma_int16 ma_waveform_sine_s16(double time, double amplitude)
65644 {
65645 return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));
65646 }
65647
65648 static float ma_waveform_square_f32(double time, double amplitude)
65649 {
65650 double f = time - (ma_int64)time;
65651 double r;
65652
65653 if (f < 0.5) {
65654 r = amplitude;
65655 } else {
65656 r = -amplitude;
65657 }
65658
65659 return (float)r;
65660 }
65661
65662 static ma_int16 ma_waveform_square_s16(double time, double amplitude)
65663 {
65664 return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude));
65665 }
65666
65667 static float ma_waveform_triangle_f32(double time, double amplitude)
65668 {
65669 double f = time - (ma_int64)time;
65670 double r;
65671
65672 r = 2 * ma_abs(2 * (f - 0.5)) - 1;
65673
65674 return (float)(r * amplitude);
65675 }
65676
65677 static ma_int16 ma_waveform_triangle_s16(double time, double amplitude)
65678 {
65679 return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));
65680 }
65681
65682 static float ma_waveform_sawtooth_f32(double time, double amplitude)
65683 {
65684 double f = time - (ma_int64)time;
65685 double r;
65686
65687 r = 2 * (f - 0.5);
65688
65689 return (float)(r * amplitude);
65690 }
65691
65692 static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)
65693 {
65694 return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));
65695 }
65696
65697 static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
65698 {
65699 ma_uint64 iFrame;
65700 ma_uint64 iChannel;
65701 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
65702 ma_uint32 bpf = bps * pWaveform->config.channels;
65703
65704 MA_ASSERT(pWaveform != NULL);
65705 MA_ASSERT(pFramesOut != NULL);
65706
65707 if (pWaveform->config.format == ma_format_f32) {
65708 float* pFramesOutF32 = (float*)pFramesOut;
65709 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65710 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
65711 pWaveform->time += pWaveform->advance;
65712
65713 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65714 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
65715 }
65716 }
65717 } else if (pWaveform->config.format == ma_format_s16) {
65718 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
65719 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65720 ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);
65721 pWaveform->time += pWaveform->advance;
65722
65723 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65724 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
65725 }
65726 }
65727 } else {
65728 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65729 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
65730 pWaveform->time += pWaveform->advance;
65731
65732 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65733 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
65734 }
65735 }
65736 }
65737 }
65738
65739 static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
65740 {
65741 ma_uint64 iFrame;
65742 ma_uint64 iChannel;
65743 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
65744 ma_uint32 bpf = bps * pWaveform->config.channels;
65745
65746 MA_ASSERT(pWaveform != NULL);
65747 MA_ASSERT(pFramesOut != NULL);
65748
65749 if (pWaveform->config.format == ma_format_f32) {
65750 float* pFramesOutF32 = (float*)pFramesOut;
65751 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65752 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
65753 pWaveform->time += pWaveform->advance;
65754
65755 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65756 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
65757 }
65758 }
65759 } else if (pWaveform->config.format == ma_format_s16) {
65760 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
65761 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65762 ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude);
65763 pWaveform->time += pWaveform->advance;
65764
65765 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65766 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
65767 }
65768 }
65769 } else {
65770 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65771 float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude);
65772 pWaveform->time += pWaveform->advance;
65773
65774 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65775 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
65776 }
65777 }
65778 }
65779 }
65780
65781 static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
65782 {
65783 ma_uint64 iFrame;
65784 ma_uint64 iChannel;
65785 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
65786 ma_uint32 bpf = bps * pWaveform->config.channels;
65787
65788 MA_ASSERT(pWaveform != NULL);
65789 MA_ASSERT(pFramesOut != NULL);
65790
65791 if (pWaveform->config.format == ma_format_f32) {
65792 float* pFramesOutF32 = (float*)pFramesOut;
65793 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65794 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
65795 pWaveform->time += pWaveform->advance;
65796
65797 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65798 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
65799 }
65800 }
65801 } else if (pWaveform->config.format == ma_format_s16) {
65802 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
65803 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65804 ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);
65805 pWaveform->time += pWaveform->advance;
65806
65807 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65808 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
65809 }
65810 }
65811 } else {
65812 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65813 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
65814 pWaveform->time += pWaveform->advance;
65815
65816 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65817 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
65818 }
65819 }
65820 }
65821 }
65822
65823 static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
65824 {
65825 ma_uint64 iFrame;
65826 ma_uint64 iChannel;
65827 ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
65828 ma_uint32 bpf = bps * pWaveform->config.channels;
65829
65830 MA_ASSERT(pWaveform != NULL);
65831 MA_ASSERT(pFramesOut != NULL);
65832
65833 if (pWaveform->config.format == ma_format_f32) {
65834 float* pFramesOutF32 = (float*)pFramesOut;
65835 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65836 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
65837 pWaveform->time += pWaveform->advance;
65838
65839 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65840 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
65841 }
65842 }
65843 } else if (pWaveform->config.format == ma_format_s16) {
65844 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
65845 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65846 ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);
65847 pWaveform->time += pWaveform->advance;
65848
65849 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65850 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
65851 }
65852 }
65853 } else {
65854 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
65855 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
65856 pWaveform->time += pWaveform->advance;
65857
65858 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
65859 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
65860 }
65861 }
65862 }
65863 }
65864
65865 MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65866 {
65867 if (pFramesRead != NULL) {
65868 *pFramesRead = 0;
65869 }
65870
65871 if (frameCount == 0) {
65872 return MA_INVALID_ARGS;
65873 }
65874
65875 if (pWaveform == NULL) {
65876 return MA_INVALID_ARGS;
65877 }
65878
65879 if (pFramesOut != NULL) {
65880 switch (pWaveform->config.type)
65881 {
65882 case ma_waveform_type_sine:
65883 {
65884 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
65885 } break;
65886
65887 case ma_waveform_type_square:
65888 {
65889 ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount);
65890 } break;
65891
65892 case ma_waveform_type_triangle:
65893 {
65894 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
65895 } break;
65896
65897 case ma_waveform_type_sawtooth:
65898 {
65899 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
65900 } break;
65901
65902 default: return MA_INVALID_OPERATION; /* Unknown waveform type. */
65903 }
65904 } else {
65905 pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
65906 }
65907
65908 if (pFramesRead != NULL) {
65909 *pFramesRead = frameCount;
65910 }
65911
65912 return MA_SUCCESS;
65913 }
65914
65915 MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex)
65916 {
65917 if (pWaveform == NULL) {
65918 return MA_INVALID_ARGS;
65919 }
65920
65921 pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */
65922
65923 return MA_SUCCESS;
65924 }
65925
65926
65927 MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
65928 {
65929 ma_noise_config config;
65930 MA_ZERO_OBJECT(&config);
65931
65932 config.format = format;
65933 config.channels = channels;
65934 config.type = type;
65935 config.seed = seed;
65936 config.amplitude = amplitude;
65937
65938 if (config.seed == 0) {
65939 config.seed = MA_DEFAULT_LCG_SEED;
65940 }
65941
65942 return config;
65943 }
65944
65945
65946 static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65947 {
65948 return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead);
65949 }
65950
65951 static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
65952 {
65953 /* No-op. Just pretend to be successful. */
65954 (void)pDataSource;
65955 (void)frameIndex;
65956 return MA_SUCCESS;
65957 }
65958
65959 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)
65960 {
65961 ma_noise* pNoise = (ma_noise*)pDataSource;
65962
65963 *pFormat = pNoise->config.format;
65964 *pChannels = pNoise->config.channels;
65965 *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */
65966 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels);
65967
65968 return MA_SUCCESS;
65969 }
65970
65971 static ma_data_source_vtable g_ma_noise_data_source_vtable =
65972 {
65973 ma_noise__data_source_on_read,
65974 ma_noise__data_source_on_seek, /* No-op for noise. */
65975 ma_noise__data_source_on_get_data_format,
65976 NULL, /* onGetCursor. No notion of a cursor for noise. */
65977 NULL, /* onGetLength. No notion of a length for noise. */
65978 NULL, /* onSetLooping */
65979 0
65980 };
65981
65982
65983 #ifndef MA_PINK_NOISE_BIN_SIZE
65984 #define MA_PINK_NOISE_BIN_SIZE 16
65985 #endif
65986
65987 typedef struct
65988 {
65989 size_t sizeInBytes;
65990 struct
65991 {
65992 size_t binOffset;
65993 size_t accumulationOffset;
65994 size_t counterOffset;
65995 } pink;
65996 struct
65997 {
65998 size_t accumulationOffset;
65999 } brownian;
66000 } ma_noise_heap_layout;
66001
66002 static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout)
66003 {
66004 MA_ASSERT(pHeapLayout != NULL);
66005
66006 MA_ZERO_OBJECT(pHeapLayout);
66007
66008 if (pConfig == NULL) {
66009 return MA_INVALID_ARGS;
66010 }
66011
66012 if (pConfig->channels == 0) {
66013 return MA_INVALID_ARGS;
66014 }
66015
66016 pHeapLayout->sizeInBytes = 0;
66017
66018 /* Pink. */
66019 if (pConfig->type == ma_noise_type_pink) {
66020 /* bin */
66021 pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes;
66022 pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels;
66023 pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE;
66024
66025 /* accumulation */
66026 pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes;
66027 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
66028
66029 /* counter */
66030 pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes;
66031 pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels;
66032 }
66033
66034 /* Brownian. */
66035 if (pConfig->type == ma_noise_type_brownian) {
66036 /* accumulation */
66037 pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes;
66038 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
66039 }
66040
66041 /* Make sure allocation size is aligned. */
66042 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
66043
66044 return MA_SUCCESS;
66045 }
66046
66047 MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes)
66048 {
66049 ma_result result;
66050 ma_noise_heap_layout heapLayout;
66051
66052 if (pHeapSizeInBytes == NULL) {
66053 return MA_INVALID_ARGS;
66054 }
66055
66056 *pHeapSizeInBytes = 0;
66057
66058 result = ma_noise_get_heap_layout(pConfig, &heapLayout);
66059 if (result != MA_SUCCESS) {
66060 return result;
66061 }
66062
66063 *pHeapSizeInBytes = heapLayout.sizeInBytes;
66064
66065 return MA_SUCCESS;
66066 }
66067
66068 MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise)
66069 {
66070 ma_result result;
66071 ma_noise_heap_layout heapLayout;
66072 ma_data_source_config dataSourceConfig;
66073 ma_uint32 iChannel;
66074
66075 if (pNoise == NULL) {
66076 return MA_INVALID_ARGS;
66077 }
66078
66079 MA_ZERO_OBJECT(pNoise);
66080
66081 result = ma_noise_get_heap_layout(pConfig, &heapLayout);
66082 if (result != MA_SUCCESS) {
66083 return result;
66084 }
66085
66086 pNoise->_pHeap = pHeap;
66087 MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes);
66088
66089 dataSourceConfig = ma_data_source_config_init();
66090 dataSourceConfig.vtable = &g_ma_noise_data_source_vtable;
66091
66092 result = ma_data_source_init(&dataSourceConfig, &pNoise->ds);
66093 if (result != MA_SUCCESS) {
66094 return result;
66095 }
66096
66097 pNoise->config = *pConfig;
66098 ma_lcg_seed(&pNoise->lcg, pConfig->seed);
66099
66100 if (pNoise->config.type == ma_noise_type_pink) {
66101 pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset);
66102 pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset);
66103 pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset);
66104
66105 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
66106 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));
66107 pNoise->state.pink.accumulation[iChannel] = 0;
66108 pNoise->state.pink.counter[iChannel] = 1;
66109 }
66110 }
66111
66112 if (pNoise->config.type == ma_noise_type_brownian) {
66113 pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset);
66114
66115 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
66116 pNoise->state.brownian.accumulation[iChannel] = 0;
66117 }
66118 }
66119
66120 return MA_SUCCESS;
66121 }
66122
66123 MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise)
66124 {
66125 ma_result result;
66126 size_t heapSizeInBytes;
66127 void* pHeap;
66128
66129 result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes);
66130 if (result != MA_SUCCESS) {
66131 return result;
66132 }
66133
66134 if (heapSizeInBytes > 0) {
66135 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
66136 if (pHeap == NULL) {
66137 return MA_OUT_OF_MEMORY;
66138 }
66139 } else {
66140 pHeap = NULL;
66141 }
66142
66143 result = ma_noise_init_preallocated(pConfig, pHeap, pNoise);
66144 if (result != MA_SUCCESS) {
66145 ma_free(pHeap, pAllocationCallbacks);
66146 return result;
66147 }
66148
66149 pNoise->_ownsHeap = MA_TRUE;
66150 return MA_SUCCESS;
66151 }
66152
66153 MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks)
66154 {
66155 if (pNoise == NULL) {
66156 return;
66157 }
66158
66159 ma_data_source_uninit(&pNoise->ds);
66160
66161 if (pNoise->_ownsHeap) {
66162 ma_free(pNoise->_pHeap, pAllocationCallbacks);
66163 }
66164 }
66165
66166 MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)
66167 {
66168 if (pNoise == NULL) {
66169 return MA_INVALID_ARGS;
66170 }
66171
66172 pNoise->config.amplitude = amplitude;
66173 return MA_SUCCESS;
66174 }
66175
66176 MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed)
66177 {
66178 if (pNoise == NULL) {
66179 return MA_INVALID_ARGS;
66180 }
66181
66182 pNoise->lcg.state = seed;
66183 return MA_SUCCESS;
66184 }
66185
66186
66187 MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)
66188 {
66189 if (pNoise == NULL) {
66190 return MA_INVALID_ARGS;
66191 }
66192
66193 /*
66194 This function should never have been implemented in the first place. Changing the type dynamically is not
66195 supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function
66196 will be removed in version 0.12.
66197 */
66198 MA_ASSERT(MA_FALSE);
66199 (void)type;
66200
66201 return MA_INVALID_OPERATION;
66202 }
66203
66204 static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
66205 {
66206 return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
66207 }
66208
66209 static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
66210 {
66211 return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
66212 }
66213
66214 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
66215 {
66216 ma_uint64 iFrame;
66217 ma_uint32 iChannel;
66218 const ma_uint32 channels = pNoise->config.channels;
66219 MA_ASSUME(channels > 0);
66220
66221 if (pNoise->config.format == ma_format_f32) {
66222 float* pFramesOutF32 = (float*)pFramesOut;
66223 if (pNoise->config.duplicateChannels) {
66224 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66225 float s = ma_noise_f32_white(pNoise);
66226 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66227 pFramesOutF32[iFrame*channels + iChannel] = s;
66228 }
66229 }
66230 } else {
66231 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66232 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66233 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);
66234 }
66235 }
66236 }
66237 } else if (pNoise->config.format == ma_format_s16) {
66238 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66239 if (pNoise->config.duplicateChannels) {
66240 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66241 ma_int16 s = ma_noise_s16_white(pNoise);
66242 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66243 pFramesOutS16[iFrame*channels + iChannel] = s;
66244 }
66245 }
66246 } else {
66247 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66248 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66249 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);
66250 }
66251 }
66252 }
66253 } else {
66254 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
66255 const ma_uint32 bpf = bps * channels;
66256
66257 if (pNoise->config.duplicateChannels) {
66258 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66259 float s = ma_noise_f32_white(pNoise);
66260 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66261 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66262 }
66263 }
66264 } else {
66265 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66266 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66267 float s = ma_noise_f32_white(pNoise);
66268 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66269 }
66270 }
66271 }
66272 }
66273
66274 return frameCount;
66275 }
66276
66277
66278 static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
66279 {
66280 unsigned int n;
66281
66282 /* Special case for odd numbers since they should happen about half the time. */
66283 if (x & 0x1) {
66284 return 0;
66285 }
66286
66287 if (x == 0) {
66288 return sizeof(x) << 3;
66289 }
66290
66291 n = 1;
66292 if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
66293 if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
66294 if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
66295 if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
66296 n -= x & 0x00000001;
66297
66298 return n;
66299 }
66300
66301 /*
66302 Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h
66303
66304 This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/
66305 */
66306 static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
66307 {
66308 double result;
66309 double binPrev;
66310 double binNext;
66311 unsigned int ibin;
66312
66313 ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1);
66314
66315 binPrev = pNoise->state.pink.bin[iChannel][ibin];
66316 binNext = ma_lcg_rand_f64(&pNoise->lcg);
66317 pNoise->state.pink.bin[iChannel][ibin] = binNext;
66318
66319 pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
66320 pNoise->state.pink.counter[iChannel] += 1;
66321
66322 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
66323 result /= 10;
66324
66325 return (float)(result * pNoise->config.amplitude);
66326 }
66327
66328 static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
66329 {
66330 return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
66331 }
66332
66333 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
66334 {
66335 ma_uint64 iFrame;
66336 ma_uint32 iChannel;
66337 const ma_uint32 channels = pNoise->config.channels;
66338 MA_ASSUME(channels > 0);
66339
66340 if (pNoise->config.format == ma_format_f32) {
66341 float* pFramesOutF32 = (float*)pFramesOut;
66342 if (pNoise->config.duplicateChannels) {
66343 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66344 float s = ma_noise_f32_pink(pNoise, 0);
66345 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66346 pFramesOutF32[iFrame*channels + iChannel] = s;
66347 }
66348 }
66349 } else {
66350 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66351 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66352 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
66353 }
66354 }
66355 }
66356 } else if (pNoise->config.format == ma_format_s16) {
66357 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66358 if (pNoise->config.duplicateChannels) {
66359 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66360 ma_int16 s = ma_noise_s16_pink(pNoise, 0);
66361 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66362 pFramesOutS16[iFrame*channels + iChannel] = s;
66363 }
66364 }
66365 } else {
66366 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66367 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66368 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
66369 }
66370 }
66371 }
66372 } else {
66373 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
66374 const ma_uint32 bpf = bps * channels;
66375
66376 if (pNoise->config.duplicateChannels) {
66377 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66378 float s = ma_noise_f32_pink(pNoise, 0);
66379 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66380 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66381 }
66382 }
66383 } else {
66384 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66385 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66386 float s = ma_noise_f32_pink(pNoise, iChannel);
66387 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66388 }
66389 }
66390 }
66391 }
66392
66393 return frameCount;
66394 }
66395
66396
66397 static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
66398 {
66399 double result;
66400
66401 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
66402 result /= 1.005; /* Don't escape the -1..1 range on average. */
66403
66404 pNoise->state.brownian.accumulation[iChannel] = result;
66405 result /= 20;
66406
66407 return (float)(result * pNoise->config.amplitude);
66408 }
66409
66410 static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
66411 {
66412 return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
66413 }
66414
66415 static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
66416 {
66417 ma_uint64 iFrame;
66418 ma_uint32 iChannel;
66419 const ma_uint32 channels = pNoise->config.channels;
66420 MA_ASSUME(channels > 0);
66421
66422 if (pNoise->config.format == ma_format_f32) {
66423 float* pFramesOutF32 = (float*)pFramesOut;
66424 if (pNoise->config.duplicateChannels) {
66425 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66426 float s = ma_noise_f32_brownian(pNoise, 0);
66427 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66428 pFramesOutF32[iFrame*channels + iChannel] = s;
66429 }
66430 }
66431 } else {
66432 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66433 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66434 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
66435 }
66436 }
66437 }
66438 } else if (pNoise->config.format == ma_format_s16) {
66439 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
66440 if (pNoise->config.duplicateChannels) {
66441 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66442 ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
66443 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66444 pFramesOutS16[iFrame*channels + iChannel] = s;
66445 }
66446 }
66447 } else {
66448 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66449 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66450 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
66451 }
66452 }
66453 }
66454 } else {
66455 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
66456 const ma_uint32 bpf = bps * channels;
66457
66458 if (pNoise->config.duplicateChannels) {
66459 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66460 float s = ma_noise_f32_brownian(pNoise, 0);
66461 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66462 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66463 }
66464 }
66465 } else {
66466 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
66467 for (iChannel = 0; iChannel < channels; iChannel += 1) {
66468 float s = ma_noise_f32_brownian(pNoise, iChannel);
66469 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
66470 }
66471 }
66472 }
66473 }
66474
66475 return frameCount;
66476 }
66477
66478 MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
66479 {
66480 ma_uint64 framesRead = 0;
66481
66482 if (pFramesRead != NULL) {
66483 *pFramesRead = 0;
66484 }
66485
66486 if (frameCount == 0) {
66487 return MA_INVALID_ARGS;
66488 }
66489
66490 if (pNoise == NULL) {
66491 return MA_INVALID_ARGS;
66492 }
66493
66494 /* 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. */
66495 if (pFramesOut == NULL) {
66496 framesRead = frameCount;
66497 } else {
66498 switch (pNoise->config.type) {
66499 case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break;
66500 case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break;
66501 case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break;
66502 default: return MA_INVALID_OPERATION; /* Unknown noise type. */
66503 }
66504 }
66505
66506 if (pFramesRead != NULL) {
66507 *pFramesRead = framesRead;
66508 }
66509
66510 return MA_SUCCESS;
66511 }
66512 #endif /* MA_NO_GENERATION */
66513
66514
66515
66516 #ifndef MA_NO_RESOURCE_MANAGER
66517 #ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS
66518 #define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000
66519 #endif
66520
66521 #ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY
66522 #define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024
66523 #endif
66524
66525 MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void)
66526 {
66527 ma_resource_manager_pipeline_notifications notifications;
66528
66529 MA_ZERO_OBJECT(&notifications);
66530
66531 return notifications;
66532 }
66533
66534 static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
66535 {
66536 if (pPipelineNotifications == NULL) {
66537 return;
66538 }
66539
66540 if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }
66541 if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }
66542 }
66543
66544 static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
66545 {
66546 if (pPipelineNotifications == NULL) {
66547 return;
66548 }
66549
66550 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }
66551 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }
66552 }
66553
66554 static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
66555 {
66556 if (pPipelineNotifications == NULL) {
66557 return;
66558 }
66559
66560 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }
66561 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }
66562 }
66563
66564
66565
66566 #ifndef MA_DEFAULT_HASH_SEED
66567 #define MA_DEFAULT_HASH_SEED 42
66568 #endif
66569
66570 /* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */
66571 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
66572 #pragma GCC diagnostic push
66573 #if __GNUC__ >= 7
66574 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
66575 #endif
66576 #endif
66577
66578 static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r)
66579 {
66580 return (x << r) | (x >> (32 - r));
66581 }
66582
66583 static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i)
66584 {
66585 ma_uint32 block;
66586
66587 /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */
66588 MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block));
66589
66590 if (ma_is_little_endian()) {
66591 return block;
66592 } else {
66593 return ma_swap_endian_uint32(block);
66594 }
66595 }
66596
66597 static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h)
66598 {
66599 h ^= h >> 16;
66600 h *= 0x85ebca6b;
66601 h ^= h >> 13;
66602 h *= 0xc2b2ae35;
66603 h ^= h >> 16;
66604
66605 return h;
66606 }
66607
66608 static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed)
66609 {
66610 const ma_uint8* data = (const ma_uint8*)key;
66611 const ma_uint32* blocks;
66612 const ma_uint8* tail;
66613 const int nblocks = len / 4;
66614 ma_uint32 h1 = seed;
66615 ma_uint32 c1 = 0xcc9e2d51;
66616 ma_uint32 c2 = 0x1b873593;
66617 ma_uint32 k1;
66618 int i;
66619
66620 blocks = (const ma_uint32 *)(data + nblocks*4);
66621
66622 for(i = -nblocks; i; i++) {
66623 k1 = ma_hash_getblock(blocks,i);
66624
66625 k1 *= c1;
66626 k1 = ma_rotl32(k1, 15);
66627 k1 *= c2;
66628
66629 h1 ^= k1;
66630 h1 = ma_rotl32(h1, 13);
66631 h1 = h1*5 + 0xe6546b64;
66632 }
66633
66634
66635 tail = (const ma_uint8*)(data + nblocks*4);
66636
66637 k1 = 0;
66638 switch(len & 3) {
66639 case 3: k1 ^= tail[2] << 16;
66640 case 2: k1 ^= tail[1] << 8;
66641 case 1: k1 ^= tail[0];
66642 k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
66643 };
66644
66645
66646 h1 ^= len;
66647 h1 = ma_hash_fmix32(h1);
66648
66649 return h1;
66650 }
66651
66652 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
66653 #pragma GCC diagnostic push
66654 #endif
66655 /* End MurmurHash3 */
66656
66657 static ma_uint32 ma_hash_string_32(const char* str)
66658 {
66659 return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED);
66660 }
66661
66662 static ma_uint32 ma_hash_string_w_32(const wchar_t* str)
66663 {
66664 return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED);
66665 }
66666
66667
66668
66669
66670 /*
66671 Basic BST Functions
66672 */
66673 static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode)
66674 {
66675 ma_resource_manager_data_buffer_node* pCurrentNode;
66676
66677 MA_ASSERT(pResourceManager != NULL);
66678 MA_ASSERT(ppDataBufferNode != NULL);
66679
66680 pCurrentNode = pResourceManager->pRootDataBufferNode;
66681 while (pCurrentNode != NULL) {
66682 if (hashedName32 == pCurrentNode->hashedName32) {
66683 break; /* Found. */
66684 } else if (hashedName32 < pCurrentNode->hashedName32) {
66685 pCurrentNode = pCurrentNode->pChildLo;
66686 } else {
66687 pCurrentNode = pCurrentNode->pChildHi;
66688 }
66689 }
66690
66691 *ppDataBufferNode = pCurrentNode;
66692
66693 if (pCurrentNode == NULL) {
66694 return MA_DOES_NOT_EXIST;
66695 } else {
66696 return MA_SUCCESS;
66697 }
66698 }
66699
66700 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)
66701 {
66702 ma_result result = MA_SUCCESS;
66703 ma_resource_manager_data_buffer_node* pCurrentNode;
66704
66705 MA_ASSERT(pResourceManager != NULL);
66706 MA_ASSERT(ppInsertPoint != NULL);
66707
66708 *ppInsertPoint = NULL;
66709
66710 if (pResourceManager->pRootDataBufferNode == NULL) {
66711 return MA_SUCCESS; /* No items. */
66712 }
66713
66714 /* 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. */
66715 pCurrentNode = pResourceManager->pRootDataBufferNode;
66716 while (pCurrentNode != NULL) {
66717 if (hashedName32 == pCurrentNode->hashedName32) {
66718 result = MA_ALREADY_EXISTS;
66719 break;
66720 } else {
66721 if (hashedName32 < pCurrentNode->hashedName32) {
66722 if (pCurrentNode->pChildLo == NULL) {
66723 result = MA_SUCCESS;
66724 break;
66725 } else {
66726 pCurrentNode = pCurrentNode->pChildLo;
66727 }
66728 } else {
66729 if (pCurrentNode->pChildHi == NULL) {
66730 result = MA_SUCCESS;
66731 break;
66732 } else {
66733 pCurrentNode = pCurrentNode->pChildHi;
66734 }
66735 }
66736 }
66737 }
66738
66739 *ppInsertPoint = pCurrentNode;
66740 return result;
66741 }
66742
66743 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)
66744 {
66745 MA_ASSERT(pResourceManager != NULL);
66746 MA_ASSERT(pDataBufferNode != NULL);
66747
66748 /* The key must have been set before calling this function. */
66749 MA_ASSERT(pDataBufferNode->hashedName32 != 0);
66750
66751 if (pInsertPoint == NULL) {
66752 /* It's the first node. */
66753 pResourceManager->pRootDataBufferNode = pDataBufferNode;
66754 } else {
66755 /* It's not the first node. It needs to be inserted. */
66756 if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) {
66757 MA_ASSERT(pInsertPoint->pChildLo == NULL);
66758 pInsertPoint->pChildLo = pDataBufferNode;
66759 } else {
66760 MA_ASSERT(pInsertPoint->pChildHi == NULL);
66761 pInsertPoint->pChildHi = pDataBufferNode;
66762 }
66763 }
66764
66765 pDataBufferNode->pParent = pInsertPoint;
66766
66767 return MA_SUCCESS;
66768 }
66769
66770 #if 0 /* Unused for now. */
66771 static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
66772 {
66773 ma_result result;
66774 ma_resource_manager_data_buffer_node* pInsertPoint;
66775
66776 MA_ASSERT(pResourceManager != NULL);
66777 MA_ASSERT(pDataBufferNode != NULL);
66778
66779 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint);
66780 if (result != MA_SUCCESS) {
66781 return MA_INVALID_ARGS;
66782 }
66783
66784 return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
66785 }
66786 #endif
66787
66788 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode)
66789 {
66790 ma_resource_manager_data_buffer_node* pCurrentNode;
66791
66792 MA_ASSERT(pDataBufferNode != NULL);
66793
66794 pCurrentNode = pDataBufferNode;
66795 while (pCurrentNode->pChildLo != NULL) {
66796 pCurrentNode = pCurrentNode->pChildLo;
66797 }
66798
66799 return pCurrentNode;
66800 }
66801
66802 static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode)
66803 {
66804 ma_resource_manager_data_buffer_node* pCurrentNode;
66805
66806 MA_ASSERT(pDataBufferNode != NULL);
66807
66808 pCurrentNode = pDataBufferNode;
66809 while (pCurrentNode->pChildHi != NULL) {
66810 pCurrentNode = pCurrentNode->pChildHi;
66811 }
66812
66813 return pCurrentNode;
66814 }
66815
66816 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)
66817 {
66818 MA_ASSERT(pDataBufferNode != NULL);
66819 MA_ASSERT(pDataBufferNode->pChildHi != NULL);
66820
66821 return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi);
66822 }
66823
66824 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)
66825 {
66826 MA_ASSERT(pDataBufferNode != NULL);
66827 MA_ASSERT(pDataBufferNode->pChildLo != NULL);
66828
66829 return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo);
66830 }
66831
66832 static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
66833 {
66834 MA_ASSERT(pResourceManager != NULL);
66835 MA_ASSERT(pDataBufferNode != NULL);
66836
66837 if (pDataBufferNode->pChildLo == NULL) {
66838 if (pDataBufferNode->pChildHi == NULL) {
66839 /* Simple case - deleting a buffer with no children. */
66840 if (pDataBufferNode->pParent == NULL) {
66841 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */
66842 pResourceManager->pRootDataBufferNode = NULL;
66843 } else {
66844 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
66845 pDataBufferNode->pParent->pChildLo = NULL;
66846 } else {
66847 pDataBufferNode->pParent->pChildHi = NULL;
66848 }
66849 }
66850 } else {
66851 /* Node has one child - pChildHi != NULL. */
66852 pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent;
66853
66854 if (pDataBufferNode->pParent == NULL) {
66855 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
66856 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi;
66857 } else {
66858 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
66859 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi;
66860 } else {
66861 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi;
66862 }
66863 }
66864 }
66865 } else {
66866 if (pDataBufferNode->pChildHi == NULL) {
66867 /* Node has one child - pChildLo != NULL. */
66868 pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;
66869
66870 if (pDataBufferNode->pParent == NULL) {
66871 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
66872 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;
66873 } else {
66874 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
66875 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;
66876 } else {
66877 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;
66878 }
66879 }
66880 } else {
66881 /* Complex case - deleting a node with two children. */
66882 ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;
66883
66884 /* 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. */
66885 pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);
66886 MA_ASSERT(pReplacementDataBufferNode != NULL);
66887
66888 /*
66889 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
66890 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
66891 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
66892 replacement node and reinserting it into the same position as the deleted node.
66893 */
66894 MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */
66895 MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */
66896
66897 if (pReplacementDataBufferNode->pChildHi == NULL) {
66898 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
66899 pReplacementDataBufferNode->pParent->pChildLo = NULL;
66900 } else {
66901 pReplacementDataBufferNode->pParent->pChildHi = NULL;
66902 }
66903 } else {
66904 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;
66905 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
66906 pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;
66907 } else {
66908 pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;
66909 }
66910 }
66911
66912
66913 /* 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 */
66914 if (pDataBufferNode->pParent != NULL) {
66915 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
66916 pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;
66917 } else {
66918 pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;
66919 }
66920 }
66921
66922 /* Now need to update the replacement node's pointers. */
66923 pReplacementDataBufferNode->pParent = pDataBufferNode->pParent;
66924 pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;
66925 pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;
66926
66927 /* Now the children of the replacement node need to have their parent pointers updated. */
66928 if (pReplacementDataBufferNode->pChildLo != NULL) {
66929 pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;
66930 }
66931 if (pReplacementDataBufferNode->pChildHi != NULL) {
66932 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;
66933 }
66934
66935 /* Now the root node needs to be updated. */
66936 if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {
66937 pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;
66938 }
66939 }
66940 }
66941
66942 return MA_SUCCESS;
66943 }
66944
66945 #if 0 /* Unused for now. */
66946 static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)
66947 {
66948 ma_result result;
66949 ma_resource_manager_data_buffer_node* pDataBufferNode;
66950
66951 result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);
66952 if (result != MA_SUCCESS) {
66953 return result; /* Could not find the data buffer. */
66954 }
66955
66956 return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);
66957 }
66958 #endif
66959
66960 static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)
66961 {
66962 return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type);
66963 }
66964
66965 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)
66966 {
66967 c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);
66968 }
66969
66970 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)
66971 {
66972 ma_uint32 refCount;
66973
66974 MA_ASSERT(pResourceManager != NULL);
66975 MA_ASSERT(pDataBufferNode != NULL);
66976
66977 (void)pResourceManager;
66978
66979 refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1;
66980
66981 if (pNewRefCount != NULL) {
66982 *pNewRefCount = refCount;
66983 }
66984
66985 return MA_SUCCESS;
66986 }
66987
66988 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)
66989 {
66990 ma_uint32 refCount;
66991
66992 MA_ASSERT(pResourceManager != NULL);
66993 MA_ASSERT(pDataBufferNode != NULL);
66994
66995 (void)pResourceManager;
66996
66997 refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;
66998
66999 if (pNewRefCount != NULL) {
67000 *pNewRefCount = refCount;
67001 }
67002
67003 return MA_SUCCESS;
67004 }
67005
67006 static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
67007 {
67008 MA_ASSERT(pResourceManager != NULL);
67009 MA_ASSERT(pDataBufferNode != NULL);
67010
67011 if (pDataBufferNode->isDataOwnedByResourceManager) {
67012 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {
67013 ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks);
67014 pDataBufferNode->data.backend.encoded.pData = NULL;
67015 pDataBufferNode->data.backend.encoded.sizeInBytes = 0;
67016 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {
67017 ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks);
67018 pDataBufferNode->data.backend.decoded.pData = NULL;
67019 pDataBufferNode->data.backend.decoded.totalFrameCount = 0;
67020 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {
67021 ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks);
67022 } else {
67023 /* Should never hit this if the node was successfully initialized. */
67024 MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);
67025 }
67026 }
67027
67028 /* The data buffer itself needs to be freed. */
67029 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
67030 }
67031
67032 static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)
67033 {
67034 MA_ASSERT(pDataBufferNode != NULL);
67035
67036 return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */
67037 }
67038
67039
67040 static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)
67041 {
67042 MA_ASSERT(pResourceManager != NULL);
67043
67044 return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;
67045 }
67046
67047
67048 typedef struct
67049 {
67050 union
67051 {
67052 ma_async_notification_event e;
67053 ma_async_notification_poll p;
67054 } backend; /* Must be the first member. */
67055 ma_resource_manager* pResourceManager;
67056 } ma_resource_manager_inline_notification;
67057
67058 static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)
67059 {
67060 MA_ASSERT(pResourceManager != NULL);
67061 MA_ASSERT(pNotification != NULL);
67062
67063 pNotification->pResourceManager = pResourceManager;
67064
67065 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67066 return ma_async_notification_event_init(&pNotification->backend.e);
67067 } else {
67068 return ma_async_notification_poll_init(&pNotification->backend.p);
67069 }
67070 }
67071
67072 static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)
67073 {
67074 MA_ASSERT(pNotification != NULL);
67075
67076 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
67077 ma_async_notification_event_uninit(&pNotification->backend.e);
67078 } else {
67079 /* No need to uninitialize a polling notification. */
67080 }
67081 }
67082
67083 static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)
67084 {
67085 MA_ASSERT(pNotification != NULL);
67086
67087 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
67088 ma_async_notification_event_wait(&pNotification->backend.e);
67089 } else {
67090 while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) {
67091 ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);
67092 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
67093 break;
67094 }
67095 }
67096 }
67097 }
67098
67099 static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)
67100 {
67101 ma_resource_manager_inline_notification_wait(pNotification);
67102 ma_resource_manager_inline_notification_uninit(pNotification);
67103 }
67104
67105
67106 static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)
67107 {
67108 MA_ASSERT(pResourceManager != NULL);
67109
67110 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67111 #ifndef MA_NO_THREADING
67112 {
67113 ma_mutex_lock(&pResourceManager->dataBufferBSTLock);
67114 }
67115 #else
67116 {
67117 MA_ASSERT(MA_FALSE); /* Should never hit this. */
67118 }
67119 #endif
67120 } else {
67121 /* Threading not enabled. Do nothing. */
67122 }
67123 }
67124
67125 static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)
67126 {
67127 MA_ASSERT(pResourceManager != NULL);
67128
67129 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67130 #ifndef MA_NO_THREADING
67131 {
67132 ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);
67133 }
67134 #else
67135 {
67136 MA_ASSERT(MA_FALSE); /* Should never hit this. */
67137 }
67138 #endif
67139 } else {
67140 /* Threading not enabled. Do nothing. */
67141 }
67142 }
67143
67144 #ifndef MA_NO_THREADING
67145 static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)
67146 {
67147 ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
67148 MA_ASSERT(pResourceManager != NULL);
67149
67150 for (;;) {
67151 ma_result result;
67152 ma_job job;
67153
67154 result = ma_resource_manager_next_job(pResourceManager, &job);
67155 if (result != MA_SUCCESS) {
67156 break;
67157 }
67158
67159 /* Terminate if we got a quit message. */
67160 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
67161 break;
67162 }
67163
67164 ma_job_process(&job);
67165 }
67166
67167 return (ma_thread_result)0;
67168 }
67169 #endif
67170
67171 MA_API ma_resource_manager_config ma_resource_manager_config_init(void)
67172 {
67173 ma_resource_manager_config config;
67174
67175 MA_ZERO_OBJECT(&config);
67176 config.decodedFormat = ma_format_unknown;
67177 config.decodedChannels = 0;
67178 config.decodedSampleRate = 0;
67179 config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */
67180 config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY;
67181
67182 /* Flags. */
67183 config.flags = 0;
67184 #ifdef MA_NO_THREADING
67185 {
67186 /* Threading is disabled at compile time so disable threading at runtime as well by default. */
67187 config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
67188 config.jobThreadCount = 0;
67189 }
67190 #endif
67191
67192 return config;
67193 }
67194
67195
67196 MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager)
67197 {
67198 ma_result result;
67199 ma_job_queue_config jobQueueConfig;
67200
67201 if (pResourceManager == NULL) {
67202 return MA_INVALID_ARGS;
67203 }
67204
67205 MA_ZERO_OBJECT(pResourceManager);
67206
67207 if (pConfig == NULL) {
67208 return MA_INVALID_ARGS;
67209 }
67210
67211 #ifndef MA_NO_THREADING
67212 {
67213 if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {
67214 return MA_INVALID_ARGS; /* Requesting too many job threads. */
67215 }
67216 }
67217 #endif
67218
67219 pResourceManager->config = *pConfig;
67220 ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks);
67221
67222 /* Get the log set up early so we can start using it as soon as possible. */
67223 if (pResourceManager->config.pLog == NULL) {
67224 result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);
67225 if (result == MA_SUCCESS) {
67226 pResourceManager->config.pLog = &pResourceManager->log;
67227 } else {
67228 pResourceManager->config.pLog = NULL; /* Logging is unavailable. */
67229 }
67230 }
67231
67232 if (pResourceManager->config.pVFS == NULL) {
67233 result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);
67234 if (result != MA_SUCCESS) {
67235 return result; /* Failed to initialize the default file system. */
67236 }
67237
67238 pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
67239 }
67240
67241 /* If threading has been disabled at compile time, enfore it at run time as well. */
67242 #ifdef MA_NO_THREADING
67243 {
67244 pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
67245 }
67246 #endif
67247
67248 /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */
67249 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
67250 pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING;
67251
67252 /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */
67253 if (pResourceManager->config.jobThreadCount > 0) {
67254 return MA_INVALID_ARGS;
67255 }
67256 }
67257
67258 /* Job queue. */
67259 jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity;
67260 jobQueueConfig.flags = 0;
67261 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {
67262 if (pResourceManager->config.jobThreadCount > 0) {
67263 return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */
67264 }
67265
67266 jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;
67267 }
67268
67269 result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue);
67270 if (result != MA_SUCCESS) {
67271 return result;
67272 }
67273
67274
67275 /* Custom decoding backends. */
67276 if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
67277 size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
67278
67279 pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
67280 if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
67281 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67282 return MA_OUT_OF_MEMORY;
67283 }
67284
67285 MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
67286
67287 pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount;
67288 pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData;
67289 }
67290
67291
67292
67293 /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */
67294 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67295 #ifndef MA_NO_THREADING
67296 {
67297 ma_uint32 iJobThread;
67298
67299 /* Data buffer lock. */
67300 result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);
67301 if (result != MA_SUCCESS) {
67302 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67303 return result;
67304 }
67305
67306 /* Create the job threads last to ensure the threads has access to valid data. */
67307 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
67308 result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);
67309 if (result != MA_SUCCESS) {
67310 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
67311 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67312 return result;
67313 }
67314 }
67315 }
67316 #else
67317 {
67318 /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */
67319 MA_ASSERT(MA_FALSE);
67320 }
67321 #endif
67322 }
67323
67324 return MA_SUCCESS;
67325 }
67326
67327
67328 static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)
67329 {
67330 MA_ASSERT(pResourceManager);
67331
67332 /* If everything was done properly, there shouldn't be any active data buffers. */
67333 while (pResourceManager->pRootDataBufferNode != NULL) {
67334 ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
67335 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
67336
67337 /* The data buffer has been removed from the BST, so now we need to free it's data. */
67338 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
67339 }
67340 }
67341
67342 MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
67343 {
67344 if (pResourceManager == NULL) {
67345 return;
67346 }
67347
67348 /*
67349 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
67350 queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.
67351 */
67352 ma_resource_manager_post_job_quit(pResourceManager);
67353
67354 /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */
67355 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67356 #ifndef MA_NO_THREADING
67357 {
67358 ma_uint32 iJobThread;
67359
67360 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
67361 ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);
67362 }
67363 }
67364 #else
67365 {
67366 MA_ASSERT(MA_FALSE); /* Should never hit this. */
67367 }
67368 #endif
67369 }
67370
67371 /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */
67372 ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);
67373
67374 /* The job queue is no longer needed. */
67375 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
67376
67377 /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */
67378 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
67379 #ifndef MA_NO_THREADING
67380 {
67381 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
67382 }
67383 #else
67384 {
67385 MA_ASSERT(MA_FALSE); /* Should never hit this. */
67386 }
67387 #endif
67388 }
67389
67390 ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);
67391
67392 if (pResourceManager->config.pLog == &pResourceManager->log) {
67393 ma_log_uninit(&pResourceManager->log);
67394 }
67395 }
67396
67397 MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager)
67398 {
67399 if (pResourceManager == NULL) {
67400 return NULL;
67401 }
67402
67403 return pResourceManager->config.pLog;
67404 }
67405
67406
67407
67408 MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)
67409 {
67410 ma_resource_manager_data_source_config config;
67411
67412 MA_ZERO_OBJECT(&config);
67413 config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
67414 config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
67415 config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
67416 config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
67417 config.isLooping = MA_FALSE;
67418
67419 return config;
67420 }
67421
67422
67423 static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)
67424 {
67425 ma_decoder_config config;
67426
67427 config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
67428 config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
67429 config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables;
67430 config.customBackendCount = pResourceManager->config.customDecodingBackendCount;
67431 config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData;
67432
67433 return config;
67434 }
67435
67436 static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)
67437 {
67438 ma_result result;
67439 ma_decoder_config config;
67440
67441 MA_ASSERT(pResourceManager != NULL);
67442 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
67443 MA_ASSERT(pDecoder != NULL);
67444
67445 config = ma_resource_manager__init_decoder_config(pResourceManager);
67446
67447 if (pFilePath != NULL) {
67448 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder);
67449 if (result != MA_SUCCESS) {
67450 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));
67451 return result;
67452 }
67453 } else {
67454 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder);
67455 if (result != MA_SUCCESS) {
67456 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
67457 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));
67458 #endif
67459 return result;
67460 }
67461 }
67462
67463 return MA_SUCCESS;
67464 }
67465
67466 static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer)
67467 {
67468 return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized);
67469 }
67470
67471 static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer)
67472 {
67473 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
67474 return NULL; /* Connector not yet initialized. */
67475 }
67476
67477 switch (pDataBuffer->pNode->data.type)
67478 {
67479 case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder;
67480 case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer;
67481 case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer;
67482
67483 case ma_resource_manager_data_supply_type_unknown:
67484 default:
67485 {
67486 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");
67487 return NULL;
67488 };
67489 };
67490 }
67491
67492 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)
67493 {
67494 ma_result result;
67495
67496 MA_ASSERT(pDataBuffer != NULL);
67497 MA_ASSERT(pConfig != NULL);
67498 MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE);
67499
67500 /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */
67501 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
67502 if (result != MA_SUCCESS && result != MA_BUSY) {
67503 return result; /* The data buffer is in an erroneous state. */
67504 }
67505
67506 /*
67507 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
67508 "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
67509 an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.
67510 */
67511 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
67512 {
67513 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
67514 {
67515 ma_decoder_config config;
67516 config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager);
67517 result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder);
67518 } break;
67519
67520 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
67521 {
67522 ma_audio_buffer_config config;
67523 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);
67524 result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
67525 } break;
67526
67527 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
67528 {
67529 ma_paged_audio_buffer_config config;
67530 config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data);
67531 result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);
67532 } break;
67533
67534 case ma_resource_manager_data_supply_type_unknown:
67535 default:
67536 {
67537 /* Unknown data supply type. Should never happen. Need to post an error here. */
67538 return MA_INVALID_ARGS;
67539 };
67540 }
67541
67542 /*
67543 Initialization of the connector is when we can fire the init notification. This will give the application access to
67544 the format/channels/rate of the data source.
67545 */
67546 if (result == MA_SUCCESS) {
67547 /*
67548 The resource manager supports the ability to set the range and loop settings via a config at
67549 initialization time. This results in an case where the ranges could be set explicitly via
67550 ma_data_source_set_*() before we get to this point here. If this happens, we'll end up
67551 hitting a case where we just override those settings which results in what feels like a bug.
67552
67553 To address this we only change the relevant properties if they're not equal to defaults. If
67554 they're equal to defaults there's no need to change them anyway. If they're *not* set to the
67555 default values, we can assume the user has set the range and loop settings via the config. If
67556 they're doing their own calls to ma_data_source_set_*() in addition to setting them via the
67557 config, that's entirely on the caller and any synchronization issue becomes their problem.
67558 */
67559 if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) {
67560 ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
67561 }
67562
67563 if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) {
67564 ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
67565 }
67566
67567 if (pConfig->isLooping != MA_FALSE) {
67568 ma_data_source_set_looping(pDataBuffer, pConfig->isLooping);
67569 }
67570
67571 ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE);
67572
67573 if (pInitNotification != NULL) {
67574 ma_async_notification_signal(pInitNotification);
67575 }
67576
67577 if (pInitFence != NULL) {
67578 ma_fence_release(pInitFence);
67579 }
67580 }
67581
67582 /* 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. */
67583 return result;
67584 }
67585
67586 static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer)
67587 {
67588 MA_ASSERT(pResourceManager != NULL);
67589 MA_ASSERT(pDataBuffer != NULL);
67590
67591 (void)pResourceManager;
67592
67593 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
67594 {
67595 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
67596 {
67597 ma_decoder_uninit(&pDataBuffer->connector.decoder);
67598 } break;
67599
67600 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
67601 {
67602 ma_audio_buffer_uninit(&pDataBuffer->connector.buffer);
67603 } break;
67604
67605 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
67606 {
67607 ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer);
67608 } break;
67609
67610 case ma_resource_manager_data_supply_type_unknown:
67611 default:
67612 {
67613 /* Unknown data supply type. Should never happen. Need to post an error here. */
67614 return MA_INVALID_ARGS;
67615 };
67616 }
67617
67618 return MA_SUCCESS;
67619 }
67620
67621 static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode)
67622 {
67623 MA_ASSERT(pDataBufferNode != NULL);
67624 return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1);
67625 }
67626
67627 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)
67628 {
67629 ma_result result;
67630 size_t dataSizeInBytes;
67631 void* pData;
67632
67633 MA_ASSERT(pResourceManager != NULL);
67634 MA_ASSERT(pDataBufferNode != NULL);
67635 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
67636
67637 result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
67638 if (result != MA_SUCCESS) {
67639 if (pFilePath != NULL) {
67640 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));
67641 } else {
67642 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
67643 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));
67644 #endif
67645 }
67646
67647 return result;
67648 }
67649
67650 pDataBufferNode->data.backend.encoded.pData = pData;
67651 pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes;
67652 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */
67653
67654 return MA_SUCCESS;
67655 }
67656
67657 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)
67658 {
67659 ma_result result = MA_SUCCESS;
67660 ma_decoder* pDecoder;
67661 ma_uint64 totalFrameCount;
67662
67663 MA_ASSERT(pResourceManager != NULL);
67664 MA_ASSERT(pDataBufferNode != NULL);
67665 MA_ASSERT(ppDecoder != NULL);
67666 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
67667
67668 *ppDecoder = NULL; /* For safety. */
67669
67670 pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks);
67671 if (pDecoder == NULL) {
67672 return MA_OUT_OF_MEMORY;
67673 }
67674
67675 result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder);
67676 if (result != MA_SUCCESS) {
67677 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
67678 return result;
67679 }
67680
67681 /*
67682 At this point we have the decoder and we now need to initialize the data supply. This will
67683 be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap
67684 allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter
67685 is used when the length of a sound is unknown until a full decode has been performed.
67686 */
67687 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
67688 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
67689 if (result != MA_SUCCESS) {
67690 return result;
67691 }
67692 } else {
67693 totalFrameCount = 0;
67694 }
67695
67696 if (totalFrameCount > 0) {
67697 /* It's a known length. The data supply is a regular decoded buffer. */
67698 ma_uint64 dataSizeInBytes;
67699 void* pData;
67700
67701 dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
67702 if (dataSizeInBytes > MA_SIZE_MAX) {
67703 ma_decoder_uninit(pDecoder);
67704 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
67705 return MA_TOO_BIG;
67706 }
67707
67708 pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
67709 if (pData == NULL) {
67710 ma_decoder_uninit(pDecoder);
67711 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
67712 return MA_OUT_OF_MEMORY;
67713 }
67714
67715 /* The buffer needs to be initialized to silence in case the caller reads from it. */
67716 ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels);
67717
67718 /* Data has been allocated and the data supply can now be initialized. */
67719 pDataBufferNode->data.backend.decoded.pData = pData;
67720 pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount;
67721 pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat;
67722 pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels;
67723 pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate;
67724 pDataBufferNode->data.backend.decoded.decodedFrameCount = 0;
67725 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */
67726 } else {
67727 /*
67728 It's an unknown length. The data supply is a paged decoded buffer. Setting this up is
67729 actually easier than the non-paged decoded buffer because we just need to initialize
67730 a ma_paged_audio_buffer object.
67731 */
67732 result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data);
67733 if (result != MA_SUCCESS) {
67734 ma_decoder_uninit(pDecoder);
67735 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
67736 return result;
67737 }
67738
67739 pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate;
67740 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0;
67741 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */
67742 }
67743
67744 *ppDecoder = pDecoder;
67745
67746 return MA_SUCCESS;
67747 }
67748
67749 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)
67750 {
67751 ma_result result = MA_SUCCESS;
67752 ma_uint64 pageSizeInFrames;
67753 ma_uint64 framesToTryReading;
67754 ma_uint64 framesRead;
67755
67756 MA_ASSERT(pResourceManager != NULL);
67757 MA_ASSERT(pDataBufferNode != NULL);
67758 MA_ASSERT(pDecoder != NULL);
67759
67760 /* We need to know the size of a page in frames to know how many frames to decode. */
67761 pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000);
67762 framesToTryReading = pageSizeInFrames;
67763
67764 /*
67765 Here is where we do the decoding of the next page. We'll run a slightly different path depending
67766 on whether or not we're using a flat or paged buffer because the allocation of the page differs
67767 between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged
67768 buffer, we need to allocate a new page and attach it to the linked list.
67769 */
67770 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode))
67771 {
67772 case ma_resource_manager_data_supply_type_decoded:
67773 {
67774 /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */
67775 void* pDst;
67776 ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount;
67777 if (framesToTryReading > framesRemaining) {
67778 framesToTryReading = framesRemaining;
67779 }
67780
67781 if (framesToTryReading > 0) {
67782 pDst = ma_offset_ptr(
67783 pDataBufferNode->data.backend.decoded.pData,
67784 pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels)
67785 );
67786 MA_ASSERT(pDst != NULL);
67787
67788 result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead);
67789 if (framesRead > 0) {
67790 pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead;
67791 }
67792 } else {
67793 framesRead = 0;
67794 }
67795 } break;
67796
67797 case ma_resource_manager_data_supply_type_decoded_paged:
67798 {
67799 /* The destination buffer is a freshly allocated page. */
67800 ma_paged_audio_buffer_page* pPage;
67801
67802 result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage);
67803 if (result != MA_SUCCESS) {
67804 return result;
67805 }
67806
67807 result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead);
67808 if (framesRead > 0) {
67809 pPage->sizeInFrames = framesRead;
67810
67811 result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);
67812 if (result == MA_SUCCESS) {
67813 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead;
67814 } else {
67815 /* Failed to append the page. Just abort and set the status to MA_AT_END. */
67816 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
67817 result = MA_AT_END;
67818 }
67819 } else {
67820 /* No frames were read. Free the page and just set the status to MA_AT_END. */
67821 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
67822 result = MA_AT_END;
67823 }
67824 } break;
67825
67826 case ma_resource_manager_data_supply_type_encoded:
67827 case ma_resource_manager_data_supply_type_unknown:
67828 default:
67829 {
67830 /* Unexpected data supply type. */
67831 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));
67832 return MA_ERROR;
67833 };
67834 }
67835
67836 if (result == MA_SUCCESS && framesRead == 0) {
67837 result = MA_AT_END;
67838 }
67839
67840 return result;
67841 }
67842
67843 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)
67844 {
67845 ma_result result = MA_SUCCESS;
67846 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
67847 ma_resource_manager_data_buffer_node* pInsertPoint;
67848
67849 if (ppDataBufferNode != NULL) {
67850 *ppDataBufferNode = NULL;
67851 }
67852
67853 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);
67854 if (result == MA_ALREADY_EXISTS) {
67855 /* The node already exists. We just need to increment the reference count. */
67856 pDataBufferNode = pInsertPoint;
67857
67858 result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);
67859 if (result != MA_SUCCESS) {
67860 return result; /* Should never happen. Failed to increment the reference count. */
67861 }
67862
67863 result = MA_ALREADY_EXISTS;
67864 goto done;
67865 } else {
67866 /*
67867 The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This
67868 needs to be done inside the critical section to ensure an uninitialization of the node
67869 does not occur before initialization on another thread.
67870 */
67871 pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);
67872 if (pDataBufferNode == NULL) {
67873 return MA_OUT_OF_MEMORY;
67874 }
67875
67876 MA_ZERO_OBJECT(pDataBufferNode);
67877 pDataBufferNode->hashedName32 = hashedName32;
67878 pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */
67879
67880 if (pExistingData == NULL) {
67881 pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */
67882 pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */
67883 pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;
67884 } else {
67885 pDataBufferNode->data = *pExistingData;
67886 pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */
67887 pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;
67888 }
67889
67890 result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
67891 if (result != MA_SUCCESS) {
67892 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
67893 return result; /* Should never happen. Failed to insert the data buffer into the BST. */
67894 }
67895
67896 /*
67897 Here is where we'll post the job, but only if we're loading asynchronously. If we're
67898 loading synchronously we'll defer loading to a later stage, outside of the critical
67899 section.
67900 */
67901 if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
67902 /* Loading asynchronously. Post the job. */
67903 ma_job job;
67904 char* pFilePathCopy = NULL;
67905 wchar_t* pFilePathWCopy = NULL;
67906
67907 /* 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. */
67908 if (pFilePath != NULL) {
67909 pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks);
67910 } else {
67911 pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks);
67912 }
67913
67914 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
67915 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
67916 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
67917 return MA_OUT_OF_MEMORY;
67918 }
67919
67920 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
67921 ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
67922 }
67923
67924 /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */
67925 if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
67926 if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
67927
67928 /* We now have everything we need to post the job to the job thread. */
67929 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE);
67930 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
67931 job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager;
67932 job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode;
67933 job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy;
67934 job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy;
67935 job.data.resourceManager.loadDataBufferNode.flags = flags;
67936 job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL;
67937 job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL;
67938 job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence;
67939 job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence;
67940
67941 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
67942 result = ma_job_process(&job);
67943 } else {
67944 result = ma_resource_manager_post_job(pResourceManager, &job);
67945 }
67946
67947 if (result != MA_SUCCESS) {
67948 /* Failed to post job. Probably ran out of memory. */
67949 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));
67950
67951 /*
67952 Fences were acquired before posting the job, but since the job was not able to
67953 be posted, we need to make sure we release them so nothing gets stuck waiting.
67954 */
67955 if (pInitFence != NULL) { ma_fence_release(pInitFence); }
67956 if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
67957
67958 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
67959 ma_resource_manager_inline_notification_uninit(pInitNotification);
67960 } else {
67961 /* 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. */
67962 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
67963 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
67964 }
67965
67966 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
67967 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
67968
67969 return result;
67970 }
67971 }
67972 }
67973
67974 done:
67975 if (ppDataBufferNode != NULL) {
67976 *ppDataBufferNode = pDataBufferNode;
67977 }
67978
67979 return result;
67980 }
67981
67982 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)
67983 {
67984 ma_result result = MA_SUCCESS;
67985 ma_bool32 nodeAlreadyExists = MA_FALSE;
67986 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
67987 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
67988
67989 if (ppDataBufferNode != NULL) {
67990 *ppDataBufferNode = NULL; /* Safety. */
67991 }
67992
67993 if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {
67994 return MA_INVALID_ARGS;
67995 }
67996
67997 /* If we're specifying existing data, it must be valid. */
67998 if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {
67999 return MA_INVALID_ARGS;
68000 }
68001
68002 /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */
68003 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
68004 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
68005 }
68006
68007 if (hashedName32 == 0) {
68008 if (pFilePath != NULL) {
68009 hashedName32 = ma_hash_string_32(pFilePath);
68010 } else {
68011 hashedName32 = ma_hash_string_w_32(pFilePathW);
68012 }
68013 }
68014
68015 /*
68016 Here is where we either increment the node's reference count or allocate a new one and add it
68017 to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is
68018 posted inside the critical section just in case the caller immediately uninitializes the node
68019 as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the
68020 node is not uninitialized before initialization.
68021 */
68022 ma_resource_manager_data_buffer_bst_lock(pResourceManager);
68023 {
68024 result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode);
68025 }
68026 ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
68027
68028 if (result == MA_ALREADY_EXISTS) {
68029 nodeAlreadyExists = MA_TRUE;
68030 result = MA_SUCCESS;
68031 } else {
68032 if (result != MA_SUCCESS) {
68033 return result;
68034 }
68035 }
68036
68037 /*
68038 If we're loading synchronously, we'll need to load everything now. When loading asynchronously,
68039 a job will have been posted inside the BST critical section so that an uninitialization can be
68040 allocated an appropriate execution order thereby preventing it from being uninitialized before
68041 the node is initialized by the decoding thread(s).
68042 */
68043 if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */
68044 if (pFilePath == NULL && pFilePathW == NULL) {
68045 /*
68046 If this path is hit, it means a buffer is being copied (i.e. initialized from only the
68047 hashed name), but that node has been freed in the meantime, probably from some other
68048 thread. This is an invalid operation.
68049 */
68050 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");
68051 result = MA_INVALID_OPERATION;
68052 goto done;
68053 }
68054
68055 if (pDataBufferNode->isDataOwnedByResourceManager) {
68056 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) {
68057 /* Loading synchronously. Load the sound in it's entirety here. */
68058 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) {
68059 /* No decoding. This is the simple case - just store the file contents in memory. */
68060 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);
68061 if (result != MA_SUCCESS) {
68062 goto done;
68063 }
68064 } else {
68065 /* Decoding. We do this the same way as we do when loading asynchronously. */
68066 ma_decoder* pDecoder;
68067 result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder);
68068 if (result != MA_SUCCESS) {
68069 goto done;
68070 }
68071
68072 /* We have the decoder, now decode page by page just like we do when loading asynchronously. */
68073 for (;;) {
68074 /* Decode next page. */
68075 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);
68076 if (result != MA_SUCCESS) {
68077 break; /* Will return MA_AT_END when the last page has been decoded. */
68078 }
68079 }
68080
68081 /* Reaching the end needs to be considered successful. */
68082 if (result == MA_AT_END) {
68083 result = MA_SUCCESS;
68084 }
68085
68086 /*
68087 At this point the data buffer is either fully decoded or some error occurred. Either
68088 way, the decoder is no longer necessary.
68089 */
68090 ma_decoder_uninit(pDecoder);
68091 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
68092 }
68093
68094 /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */
68095 c89atomic_exchange_i32(&pDataBufferNode->result, result);
68096 } else {
68097 /* Loading asynchronously. We may need to wait for initialization. */
68098 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68099 ma_resource_manager_inline_notification_wait(&initNotification);
68100 }
68101 }
68102 } else {
68103 /* The data is not managed by the resource manager so there's nothing else to do. */
68104 MA_ASSERT(pExistingData != NULL);
68105 }
68106 }
68107
68108 done:
68109 /* If we failed to initialize the data buffer we need to free it. */
68110 if (result != MA_SUCCESS) {
68111 if (nodeAlreadyExists == MA_FALSE) {
68112 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
68113 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
68114 }
68115 }
68116
68117 /*
68118 The init notification needs to be uninitialized. This will be used if the node does not already
68119 exist, and we've specified ASYNC | WAIT_INIT.
68120 */
68121 if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
68122 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68123 ma_resource_manager_inline_notification_uninit(&initNotification);
68124 }
68125 }
68126
68127 if (ppDataBufferNode != NULL) {
68128 *ppDataBufferNode = pDataBufferNode;
68129 }
68130
68131 return result;
68132 }
68133
68134 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)
68135 {
68136 ma_result result = MA_SUCCESS;
68137 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. */
68138 ma_uint32 hashedName32 = 0;
68139
68140 if (pResourceManager == NULL) {
68141 return MA_INVALID_ARGS;
68142 }
68143
68144 if (pDataBufferNode == NULL) {
68145 if (pName == NULL && pNameW == NULL) {
68146 return MA_INVALID_ARGS;
68147 }
68148
68149 if (pName != NULL) {
68150 hashedName32 = ma_hash_string_32(pName);
68151 } else {
68152 hashedName32 = ma_hash_string_w_32(pNameW);
68153 }
68154 }
68155
68156 /*
68157 The first thing to do is decrement the reference counter of the node. Then, if the reference
68158 count is zero, we need to free the node. If the node is still in the process of loading, we'll
68159 need to post a job to the job queue to free the node. Otherwise we'll just do it here.
68160 */
68161 ma_resource_manager_data_buffer_bst_lock(pResourceManager);
68162 {
68163 /* Might need to find the node. Must be done inside the critical section. */
68164 if (pDataBufferNode == NULL) {
68165 result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);
68166 if (result != MA_SUCCESS) {
68167 goto stage2; /* Couldn't find the node. */
68168 }
68169 }
68170
68171 result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);
68172 if (result != MA_SUCCESS) {
68173 goto stage2; /* Should never happen. */
68174 }
68175
68176 if (refCount == 0) {
68177 result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
68178 if (result != MA_SUCCESS) {
68179 goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */
68180 }
68181 }
68182 }
68183 ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
68184
68185 stage2:
68186 if (result != MA_SUCCESS) {
68187 return result;
68188 }
68189
68190 /*
68191 Here is where we need to free the node. We don't want to do this inside the critical section
68192 above because we want to keep that as small as possible for multi-threaded efficiency.
68193 */
68194 if (refCount == 0) {
68195 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
68196 /* The sound is still loading. We need to delay the freeing of the node to a safe time. */
68197 ma_job job;
68198
68199 /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */
68200 c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);
68201
68202 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE);
68203 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
68204 job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager;
68205 job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode;
68206
68207 result = ma_resource_manager_post_job(pResourceManager, &job);
68208 if (result != MA_SUCCESS) {
68209 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));
68210 return result;
68211 }
68212
68213 /* If we don't support threading, process the job queue here. */
68214 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
68215 while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
68216 result = ma_resource_manager_process_next_job(pResourceManager);
68217 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
68218 result = MA_SUCCESS;
68219 break;
68220 }
68221 }
68222 } else {
68223 /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */
68224 }
68225 } else {
68226 /* The sound isn't loading so we can just free the node here. */
68227 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
68228 }
68229 }
68230
68231 return result;
68232 }
68233
68234
68235
68236 static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)
68237 {
68238 MA_ASSERT(pDataBuffer != NULL);
68239 return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);
68240 }
68241
68242 static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
68243 {
68244 return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
68245 }
68246
68247 static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
68248 {
68249 return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex);
68250 }
68251
68252 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)
68253 {
68254 return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
68255 }
68256
68257 static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
68258 {
68259 return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor);
68260 }
68261
68262 static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
68263 {
68264 return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength);
68265 }
68266
68267 static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
68268 {
68269 ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource;
68270 MA_ASSERT(pDataBuffer != NULL);
68271
68272 c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
68273
68274 /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */
68275 ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
68276
68277 return MA_SUCCESS;
68278 }
68279
68280 static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
68281 {
68282 ma_resource_manager_data_buffer_cb__read_pcm_frames,
68283 ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,
68284 ma_resource_manager_data_buffer_cb__get_data_format,
68285 ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,
68286 ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames,
68287 ma_resource_manager_data_buffer_cb__set_looping,
68288 0
68289 };
68290
68291 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)
68292 {
68293 ma_result result = MA_SUCCESS;
68294 ma_resource_manager_data_buffer_node* pDataBufferNode;
68295 ma_data_source_config dataSourceConfig;
68296 ma_bool32 async;
68297 ma_uint32 flags;
68298 ma_resource_manager_pipeline_notifications notifications;
68299
68300 if (pDataBuffer == NULL) {
68301 if (pConfig != NULL && pConfig->pNotifications != NULL) {
68302 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
68303 }
68304
68305 return MA_INVALID_ARGS;
68306 }
68307
68308 MA_ZERO_OBJECT(pDataBuffer);
68309
68310 if (pConfig == NULL) {
68311 return MA_INVALID_ARGS;
68312 }
68313
68314 if (pConfig->pNotifications != NULL) {
68315 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. */
68316 } else {
68317 MA_ZERO_OBJECT(&notifications);
68318 }
68319
68320 /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */
68321 flags = pConfig->flags;
68322 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
68323 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
68324 }
68325
68326 async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
68327
68328 /*
68329 Fences need to be acquired before doing anything. These must be aquired and released outside of
68330 the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
68331 data buffer has completed initialization.
68332
68333 When loading asynchronously, the node acquisition routine below will acquire the fences on this
68334 thread and then release them on the async thread when the operation is complete.
68335
68336 These fences are always released at the "done" tag at the end of this function. They'll be
68337 acquired a second if loading asynchronously. This double acquisition system is just done to
68338 simplify code maintanence.
68339 */
68340 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
68341 {
68342 /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */
68343 result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
68344 if (result != MA_SUCCESS) {
68345 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
68346 goto done;
68347 }
68348
68349 dataSourceConfig = ma_data_source_config_init();
68350 dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
68351
68352 result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
68353 if (result != MA_SUCCESS) {
68354 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
68355 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
68356 goto done;
68357 }
68358
68359 pDataBuffer->pResourceManager = pResourceManager;
68360 pDataBuffer->pNode = pDataBufferNode;
68361 pDataBuffer->flags = flags;
68362 pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
68363
68364 /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
68365 if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
68366 /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
68367 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL);
68368 c89atomic_exchange_i32(&pDataBuffer->result, result);
68369
68370 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
68371 goto done;
68372 } else {
68373 /* 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. */
68374 ma_job job;
68375 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
68376
68377 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68378 ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
68379 }
68380
68381 /*
68382 The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
68383 worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other
68384 than MA_BUSY, it'll assume an error and fall through to an early exit.
68385 */
68386 c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
68387
68388 /* Acquire fences a second time. These will be released by the async thread. */
68389 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
68390
68391 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER);
68392 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
68393 job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer;
68394 job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
68395 job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
68396 job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence;
68397 job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence;
68398 job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
68399 job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
68400 job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
68401 job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
68402 job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping;
68403
68404 /* If we need to wait for initialization to complete we can just process the job in place. */
68405 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68406 result = ma_job_process(&job);
68407 } else {
68408 result = ma_resource_manager_post_job(pResourceManager, &job);
68409 }
68410
68411 if (result != MA_SUCCESS) {
68412 /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */
68413 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));
68414 c89atomic_exchange_i32(&pDataBuffer->result, result);
68415
68416 /* Release the fences after the result has been set on the data buffer. */
68417 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
68418 } else {
68419 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68420 ma_resource_manager_inline_notification_wait(&initNotification);
68421
68422 if (notifications.init.pNotification != NULL) {
68423 ma_async_notification_signal(notifications.init.pNotification);
68424 }
68425
68426 /* NOTE: Do not release the init fence here. It will have been done by the job. */
68427
68428 /* Make sure we return an error if initialization failed on the async thread. */
68429 result = ma_resource_manager_data_buffer_result(pDataBuffer);
68430 if (result == MA_BUSY) {
68431 result = MA_SUCCESS;
68432 }
68433 }
68434 }
68435
68436 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
68437 ma_resource_manager_inline_notification_uninit(&initNotification);
68438 }
68439 }
68440
68441 if (result != MA_SUCCESS) {
68442 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
68443 goto done;
68444 }
68445 }
68446 done:
68447 if (result == MA_SUCCESS) {
68448 if (pConfig->initialSeekPointInPCMFrames > 0) {
68449 ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames);
68450 }
68451 }
68452
68453 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
68454
68455 return result;
68456 }
68457
68458 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)
68459 {
68460 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer);
68461 }
68462
68463 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)
68464 {
68465 ma_resource_manager_data_source_config config;
68466
68467 config = ma_resource_manager_data_source_config_init();
68468 config.pFilePath = pFilePath;
68469 config.flags = flags;
68470 config.pNotifications = pNotifications;
68471
68472 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
68473 }
68474
68475 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)
68476 {
68477 ma_resource_manager_data_source_config config;
68478
68479 config = ma_resource_manager_data_source_config_init();
68480 config.pFilePathW = pFilePath;
68481 config.flags = flags;
68482 config.pNotifications = pNotifications;
68483
68484 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
68485 }
68486
68487 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)
68488 {
68489 ma_resource_manager_data_source_config config;
68490
68491 if (pExistingDataBuffer == NULL) {
68492 return MA_INVALID_ARGS;
68493 }
68494
68495 MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */
68496
68497 config = ma_resource_manager_data_source_config_init();
68498 config.flags = pExistingDataBuffer->flags;
68499
68500 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer);
68501 }
68502
68503 static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)
68504 {
68505 MA_ASSERT(pDataBuffer != NULL);
68506
68507 /* The connector should be uninitialized first. */
68508 ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);
68509
68510 /* With the connector uninitialized we can unacquire the node. */
68511 ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);
68512
68513 /* The base data source needs to be uninitialized as well. */
68514 ma_data_source_uninit(&pDataBuffer->ds);
68515
68516 return MA_SUCCESS;
68517 }
68518
68519 MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer)
68520 {
68521 ma_result result;
68522
68523 if (pDataBuffer == NULL) {
68524 return MA_INVALID_ARGS;
68525 }
68526
68527 if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {
68528 /* The data buffer can be deleted synchronously. */
68529 return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
68530 } else {
68531 /*
68532 The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will
68533 be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event
68534 to get processed before returning.
68535 */
68536 ma_resource_manager_inline_notification notification;
68537 ma_job job;
68538
68539 /*
68540 We need to mark the node as unavailable so we don't try reading from it anymore, but also to
68541 let the loading thread know that it needs to abort it's loading procedure.
68542 */
68543 c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);
68544
68545 result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, &notification);
68546 if (result != MA_SUCCESS) {
68547 return result; /* Failed to create the notification. This should rarely, if ever, happen. */
68548 }
68549
68550 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER);
68551 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
68552 job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer;
68553 job.data.resourceManager.freeDataBuffer.pDoneNotification = &notification;
68554 job.data.resourceManager.freeDataBuffer.pDoneFence = NULL;
68555
68556 result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
68557 if (result != MA_SUCCESS) {
68558 ma_resource_manager_inline_notification_uninit(&notification);
68559 return result;
68560 }
68561
68562 ma_resource_manager_inline_notification_wait_and_uninit(&notification);
68563 }
68564
68565 return result;
68566 }
68567
68568 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)
68569 {
68570 ma_result result = MA_SUCCESS;
68571 ma_uint64 framesRead = 0;
68572 ma_bool32 isDecodedBufferBusy = MA_FALSE;
68573
68574 /* Safety. */
68575 if (pFramesRead != NULL) {
68576 *pFramesRead = 0;
68577 }
68578
68579 if (frameCount == 0) {
68580 return MA_INVALID_ARGS;
68581 }
68582
68583 /*
68584 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
68585 it's been uninitialized or is in the process of uninitializing.
68586 */
68587 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
68588
68589 /* If the node is not initialized we need to abort with a busy code. */
68590 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
68591 return MA_BUSY; /* Still loading. */
68592 }
68593
68594 /*
68595 If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's
68596 a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If
68597 this happens, we need to keep the seek scheduled and return MA_BUSY.
68598 */
68599 if (pDataBuffer->seekToCursorOnNextRead) {
68600 pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
68601
68602 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
68603 if (result != MA_SUCCESS) {
68604 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) {
68605 pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */
68606 return MA_BUSY;
68607 }
68608
68609 return result;
68610 }
68611 }
68612
68613 /*
68614 For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot
68615 exceed this amount. We'll read as much as we can, and then return MA_BUSY.
68616 */
68617 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {
68618 ma_uint64 availableFrames;
68619
68620 isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
68621
68622 if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
68623 /* Don't try reading more than the available frame count. */
68624 if (frameCount > availableFrames) {
68625 frameCount = availableFrames;
68626
68627 /*
68628 If there's no frames available we want to set the status to MA_AT_END. The logic below
68629 will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
68630 is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
68631 is 0 because that'll result in a situation where it's possible MA_AT_END won't get
68632 returned.
68633 */
68634 if (frameCount == 0) {
68635 result = MA_AT_END;
68636 }
68637 } else {
68638 isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
68639 }
68640 }
68641 }
68642
68643 /* Don't attempt to read anything if we've got no frames available. */
68644 if (frameCount > 0) {
68645 result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead);
68646 }
68647
68648 /*
68649 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
68650 as at the end and terminate decoding.
68651 */
68652 if (result == MA_AT_END) {
68653 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
68654 result = MA_BUSY;
68655 }
68656 }
68657
68658 if (isDecodedBufferBusy) {
68659 result = MA_BUSY;
68660 }
68661
68662 if (pFramesRead != NULL) {
68663 *pFramesRead = framesRead;
68664 }
68665
68666 if (result == MA_SUCCESS && framesRead == 0) {
68667 result = MA_AT_END;
68668 }
68669
68670 return result;
68671 }
68672
68673 MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex)
68674 {
68675 ma_result result;
68676
68677 /* We cannot be using the data source after it's been uninitialized. */
68678 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
68679
68680 /* If we haven't yet got a connector we need to abort. */
68681 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
68682 pDataBuffer->seekTargetInPCMFrames = frameIndex;
68683 pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
68684 return MA_BUSY; /* Still loading. */
68685 }
68686
68687 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);
68688 if (result != MA_SUCCESS) {
68689 return result;
68690 }
68691
68692 pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */
68693 pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
68694
68695 return MA_SUCCESS;
68696 }
68697
68698 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)
68699 {
68700 /* We cannot be using the data source after it's been uninitialized. */
68701 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
68702
68703 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
68704 {
68705 case ma_resource_manager_data_supply_type_encoded:
68706 {
68707 return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
68708 };
68709
68710 case ma_resource_manager_data_supply_type_decoded:
68711 {
68712 *pFormat = pDataBuffer->pNode->data.backend.decoded.format;
68713 *pChannels = pDataBuffer->pNode->data.backend.decoded.channels;
68714 *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate;
68715 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);
68716 return MA_SUCCESS;
68717 };
68718
68719 case ma_resource_manager_data_supply_type_decoded_paged:
68720 {
68721 *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format;
68722 *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels;
68723 *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate;
68724 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);
68725 return MA_SUCCESS;
68726 };
68727
68728 case ma_resource_manager_data_supply_type_unknown:
68729 {
68730 return MA_BUSY; /* Still loading. */
68731 };
68732
68733 default:
68734 {
68735 /* Unknown supply type. Should never hit this. */
68736 return MA_INVALID_ARGS;
68737 }
68738 }
68739 }
68740
68741 MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor)
68742 {
68743 /* We cannot be using the data source after it's been uninitialized. */
68744 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
68745
68746 if (pDataBuffer == NULL || pCursor == NULL) {
68747 return MA_INVALID_ARGS;
68748 }
68749
68750 *pCursor = 0;
68751
68752 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
68753 {
68754 case ma_resource_manager_data_supply_type_encoded:
68755 {
68756 return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor);
68757 };
68758
68759 case ma_resource_manager_data_supply_type_decoded:
68760 {
68761 return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor);
68762 };
68763
68764 case ma_resource_manager_data_supply_type_decoded_paged:
68765 {
68766 return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor);
68767 };
68768
68769 case ma_resource_manager_data_supply_type_unknown:
68770 {
68771 return MA_BUSY;
68772 };
68773
68774 default:
68775 {
68776 return MA_INVALID_ARGS;
68777 }
68778 }
68779 }
68780
68781 MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength)
68782 {
68783 /* We cannot be using the data source after it's been uninitialized. */
68784 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
68785
68786 if (pDataBuffer == NULL || pLength == NULL) {
68787 return MA_INVALID_ARGS;
68788 }
68789
68790 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
68791 return MA_BUSY; /* Still loading. */
68792 }
68793
68794 return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength);
68795 }
68796
68797 MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer)
68798 {
68799 if (pDataBuffer == NULL) {
68800 return MA_INVALID_ARGS;
68801 }
68802
68803 return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */
68804 }
68805
68806 MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping)
68807 {
68808 return ma_data_source_set_looping(pDataBuffer, isLooping);
68809 }
68810
68811 MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer)
68812 {
68813 return ma_data_source_is_looping(pDataBuffer);
68814 }
68815
68816 MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames)
68817 {
68818 if (pAvailableFrames == NULL) {
68819 return MA_INVALID_ARGS;
68820 }
68821
68822 *pAvailableFrames = 0;
68823
68824 if (pDataBuffer == NULL) {
68825 return MA_INVALID_ARGS;
68826 }
68827
68828 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
68829 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
68830 return MA_BUSY;
68831 } else {
68832 return MA_INVALID_OPERATION; /* No connector. */
68833 }
68834 }
68835
68836 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
68837 {
68838 case ma_resource_manager_data_supply_type_encoded:
68839 {
68840 return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames);
68841 };
68842
68843 case ma_resource_manager_data_supply_type_decoded:
68844 {
68845 return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames);
68846 };
68847
68848 case ma_resource_manager_data_supply_type_decoded_paged:
68849 {
68850 ma_uint64 cursor;
68851 ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor);
68852
68853 if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) {
68854 *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor;
68855 } else {
68856 *pAvailableFrames = 0;
68857 }
68858
68859 return MA_SUCCESS;
68860 };
68861
68862 case ma_resource_manager_data_supply_type_unknown:
68863 default:
68864 {
68865 /* Unknown supply type. Should never hit this. */
68866 return MA_INVALID_ARGS;
68867 }
68868 }
68869 }
68870
68871 MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)
68872 {
68873 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);
68874 }
68875
68876 MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)
68877 {
68878 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);
68879 }
68880
68881
68882 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)
68883 {
68884 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);
68885 }
68886
68887 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)
68888 {
68889 ma_resource_manager_data_supply data;
68890 data.type = ma_resource_manager_data_supply_type_decoded;
68891 data.backend.decoded.pData = pData;
68892 data.backend.decoded.totalFrameCount = frameCount;
68893 data.backend.decoded.format = format;
68894 data.backend.decoded.channels = channels;
68895 data.backend.decoded.sampleRate = sampleRate;
68896
68897 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
68898 }
68899
68900 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)
68901 {
68902 return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate);
68903 }
68904
68905 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)
68906 {
68907 return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate);
68908 }
68909
68910
68911 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)
68912 {
68913 ma_resource_manager_data_supply data;
68914 data.type = ma_resource_manager_data_supply_type_encoded;
68915 data.backend.encoded.pData = pData;
68916 data.backend.encoded.sizeInBytes = sizeInBytes;
68917
68918 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
68919 }
68920
68921 MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes)
68922 {
68923 return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes);
68924 }
68925
68926 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)
68927 {
68928 return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes);
68929 }
68930
68931
68932 MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath)
68933 {
68934 return ma_resource_manager_unregister_data(pResourceManager, pFilePath);
68935 }
68936
68937 MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath)
68938 {
68939 return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath);
68940 }
68941
68942 MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName)
68943 {
68944 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL);
68945 }
68946
68947 MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName)
68948 {
68949 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName);
68950 }
68951
68952
68953 static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream)
68954 {
68955 MA_ASSERT(pDataStream != NULL);
68956 return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1);
68957 }
68958
68959 static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream)
68960 {
68961 MA_ASSERT(pDataStream != NULL);
68962 return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd);
68963 }
68964
68965 static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream)
68966 {
68967 MA_ASSERT(pDataStream != NULL);
68968 return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter);
68969 }
68970
68971
68972 static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
68973 {
68974 return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead);
68975 }
68976
68977 static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
68978 {
68979 return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex);
68980 }
68981
68982 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)
68983 {
68984 return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
68985 }
68986
68987 static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
68988 {
68989 return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor);
68990 }
68991
68992 static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
68993 {
68994 return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength);
68995 }
68996
68997 static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
68998 {
68999 ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource;
69000 MA_ASSERT(pDataStream != NULL);
69001
69002 c89atomic_exchange_32(&pDataStream->isLooping, isLooping);
69003
69004 return MA_SUCCESS;
69005 }
69006
69007 static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
69008 {
69009 ma_resource_manager_data_stream_cb__read_pcm_frames,
69010 ma_resource_manager_data_stream_cb__seek_to_pcm_frame,
69011 ma_resource_manager_data_stream_cb__get_data_format,
69012 ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames,
69013 ma_resource_manager_data_stream_cb__get_length_in_pcm_frames,
69014 ma_resource_manager_data_stream_cb__set_looping,
69015 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/
69016 };
69017
69018 static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor)
69019 {
69020 /* Loop if possible. */
69021 if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {
69022 absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;
69023 }
69024
69025 c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);
69026 }
69027
69028 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)
69029 {
69030 ma_result result;
69031 ma_data_source_config dataSourceConfig;
69032 char* pFilePathCopy = NULL;
69033 wchar_t* pFilePathWCopy = NULL;
69034 ma_job job;
69035 ma_bool32 waitBeforeReturning = MA_FALSE;
69036 ma_resource_manager_inline_notification waitNotification;
69037 ma_resource_manager_pipeline_notifications notifications;
69038
69039 if (pDataStream == NULL) {
69040 if (pConfig != NULL && pConfig->pNotifications != NULL) {
69041 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
69042 }
69043
69044 return MA_INVALID_ARGS;
69045 }
69046
69047 MA_ZERO_OBJECT(pDataStream);
69048
69049 if (pConfig == NULL) {
69050 return MA_INVALID_ARGS;
69051 }
69052
69053 if (pConfig->pNotifications != NULL) {
69054 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. */
69055 } else {
69056 MA_ZERO_OBJECT(&notifications);
69057 }
69058
69059 dataSourceConfig = ma_data_source_config_init();
69060 dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;
69061
69062 result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
69063 if (result != MA_SUCCESS) {
69064 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69065 return result;
69066 }
69067
69068 pDataStream->pResourceManager = pResourceManager;
69069 pDataStream->flags = pConfig->flags;
69070 pDataStream->result = MA_BUSY;
69071
69072 ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
69073 ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
69074 ma_data_source_set_looping(pDataStream, pConfig->isLooping);
69075
69076 if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {
69077 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69078 return MA_INVALID_ARGS;
69079 }
69080
69081 /* 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. */
69082
69083 /* 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. */
69084 if (pConfig->pFilePath != NULL) {
69085 pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks);
69086 } else {
69087 pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks);
69088 }
69089
69090 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
69091 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69092 return MA_OUT_OF_MEMORY;
69093 }
69094
69095 /*
69096 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
69097 can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same.
69098 */
69099 if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
69100 waitBeforeReturning = MA_TRUE;
69101 ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
69102 }
69103
69104 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
69105
69106 /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */
69107 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames);
69108
69109 /* 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. */
69110 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM);
69111 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69112 job.data.resourceManager.loadDataStream.pDataStream = pDataStream;
69113 job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy;
69114 job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy;
69115 job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames;
69116 job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
69117 job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence;
69118 result = ma_resource_manager_post_job(pResourceManager, &job);
69119 if (result != MA_SUCCESS) {
69120 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
69121 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
69122
69123 if (waitBeforeReturning) {
69124 ma_resource_manager_inline_notification_uninit(&waitNotification);
69125 }
69126
69127 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
69128 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
69129 return result;
69130 }
69131
69132 /* Wait if needed. */
69133 if (waitBeforeReturning) {
69134 ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
69135
69136 if (notifications.init.pNotification != NULL) {
69137 ma_async_notification_signal(notifications.init.pNotification);
69138 }
69139
69140 /*
69141 If there was an error during initialization make sure we return that result here. We don't want to do this
69142 if we're not waiting because it will most likely be in a busy state.
69143 */
69144 if (pDataStream->result != MA_SUCCESS) {
69145 return pDataStream->result;
69146 }
69147
69148 /* NOTE: Do not release pInitFence here. That will be done by the job. */
69149 }
69150
69151 return MA_SUCCESS;
69152 }
69153
69154 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)
69155 {
69156 ma_resource_manager_data_source_config config;
69157
69158 config = ma_resource_manager_data_source_config_init();
69159 config.pFilePath = pFilePath;
69160 config.flags = flags;
69161 config.pNotifications = pNotifications;
69162
69163 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
69164 }
69165
69166 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)
69167 {
69168 ma_resource_manager_data_source_config config;
69169
69170 config = ma_resource_manager_data_source_config_init();
69171 config.pFilePathW = pFilePath;
69172 config.flags = flags;
69173 config.pNotifications = pNotifications;
69174
69175 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
69176 }
69177
69178 MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream)
69179 {
69180 ma_resource_manager_inline_notification freeEvent;
69181 ma_job job;
69182
69183 if (pDataStream == NULL) {
69184 return MA_INVALID_ARGS;
69185 }
69186
69187 /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */
69188 c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE);
69189
69190 /*
69191 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
69192 to wait for it to complete before returning which means we need an event.
69193 */
69194 ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent);
69195
69196 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM);
69197 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69198 job.data.resourceManager.freeDataStream.pDataStream = pDataStream;
69199 job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent;
69200 job.data.resourceManager.freeDataStream.pDoneFence = NULL;
69201 ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
69202
69203 /* We need to wait for the job to finish processing before we return. */
69204 ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);
69205
69206 return MA_SUCCESS;
69207 }
69208
69209
69210 static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream)
69211 {
69212 MA_ASSERT(pDataStream != NULL);
69213 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
69214
69215 return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000);
69216 }
69217
69218 static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor)
69219 {
69220 MA_ASSERT(pDataStream != NULL);
69221 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
69222 MA_ASSERT(pageIndex == 0 || pageIndex == 1);
69223
69224 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));
69225 }
69226
69227 static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex)
69228 {
69229 ma_result result = MA_SUCCESS;
69230 ma_uint64 pageSizeInFrames;
69231 ma_uint64 totalFramesReadForThisPage = 0;
69232 void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0);
69233
69234 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
69235
69236 /* The decoder needs to inherit the stream's looping and range state. */
69237 {
69238 ma_uint64 rangeBeg;
69239 ma_uint64 rangeEnd;
69240 ma_uint64 loopPointBeg;
69241 ma_uint64 loopPointEnd;
69242
69243 ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream));
69244
69245 ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);
69246 ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);
69247
69248 ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);
69249 ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);
69250 }
69251
69252 /* Just read straight from the decoder. It will deal with ranges and looping for us. */
69253 result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);
69254 if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {
69255 c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);
69256 }
69257
69258 c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
69259 c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
69260 }
69261
69262 static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
69263 {
69264 ma_uint32 iPage;
69265
69266 MA_ASSERT(pDataStream != NULL);
69267
69268 for (iPage = 0; iPage < 2; iPage += 1) {
69269 ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
69270 }
69271 }
69272
69273
69274 static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)
69275 {
69276 ma_uint64 framesAvailable;
69277 ma_uint64 frameCount = 0;
69278
69279 /* We cannot be using the data source after it's been uninitialized. */
69280 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69281
69282 if (pFrameCount != NULL) {
69283 frameCount = *pFrameCount;
69284 *pFrameCount = 0;
69285 }
69286 if (ppFramesOut != NULL) {
69287 *ppFramesOut = NULL;
69288 }
69289
69290 if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
69291 return MA_INVALID_ARGS;
69292 }
69293
69294 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
69295 return MA_INVALID_OPERATION;
69296 }
69297
69298 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
69299 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
69300 return MA_BUSY;
69301 }
69302
69303 /* If the page we're on is invalid it means we've caught up to the job thread. */
69304 if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
69305 framesAvailable = 0;
69306 } else {
69307 /*
69308 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
69309 that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.
69310 */
69311 ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
69312 MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
69313
69314 framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
69315 }
69316
69317 /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */
69318 if (framesAvailable == 0) {
69319 if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
69320 return MA_AT_END;
69321 } else {
69322 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. */
69323 }
69324 }
69325
69326 MA_ASSERT(framesAvailable > 0);
69327
69328 if (frameCount > framesAvailable) {
69329 frameCount = framesAvailable;
69330 }
69331
69332 *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
69333 *pFrameCount = frameCount;
69334
69335 return MA_SUCCESS;
69336 }
69337
69338 static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)
69339 {
69340 ma_uint32 newRelativeCursor;
69341 ma_uint32 pageSizeInFrames;
69342 ma_job job;
69343
69344 /* We cannot be using the data source after it's been uninitialized. */
69345 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69346
69347 if (pDataStream == NULL) {
69348 return MA_INVALID_ARGS;
69349 }
69350
69351 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
69352 return MA_INVALID_OPERATION;
69353 }
69354
69355 /* The frame count should always fit inside a 32-bit integer. */
69356 if (frameCount > 0xFFFFFFFF) {
69357 return MA_INVALID_ARGS;
69358 }
69359
69360 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
69361
69362 /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */
69363 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
69364
69365 /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */
69366 newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
69367
69368 /* 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. */
69369 if (newRelativeCursor >= pageSizeInFrames) {
69370 newRelativeCursor -= pageSizeInFrames;
69371
69372 /* Here is where we post the job start decoding. */
69373 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM);
69374 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69375 job.data.resourceManager.pageDataStream.pDataStream = pDataStream;
69376 job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex;
69377
69378 /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */
69379 c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
69380
69381 /* Before posting the job we need to make sure we set some state. */
69382 pDataStream->relativeCursor = newRelativeCursor;
69383 pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
69384 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
69385 } else {
69386 /* We haven't moved into a new page so we can just move the cursor forward. */
69387 pDataStream->relativeCursor = newRelativeCursor;
69388 return MA_SUCCESS;
69389 }
69390 }
69391
69392
69393 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)
69394 {
69395 ma_result result = MA_SUCCESS;
69396 ma_uint64 totalFramesProcessed;
69397 ma_format format;
69398 ma_uint32 channels;
69399
69400 /* Safety. */
69401 if (pFramesRead != NULL) {
69402 *pFramesRead = 0;
69403 }
69404
69405 if (frameCount == 0) {
69406 return MA_INVALID_ARGS;
69407 }
69408
69409 /* We cannot be using the data source after it's been uninitialized. */
69410 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69411
69412 if (pDataStream == NULL) {
69413 return MA_INVALID_ARGS;
69414 }
69415
69416 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
69417 return MA_INVALID_OPERATION;
69418 }
69419
69420 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
69421 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
69422 return MA_BUSY;
69423 }
69424
69425 ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0);
69426
69427 /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */
69428 totalFramesProcessed = 0;
69429 while (totalFramesProcessed < frameCount) {
69430 void* pMappedFrames;
69431 ma_uint64 mappedFrameCount;
69432
69433 mappedFrameCount = frameCount - totalFramesProcessed;
69434 result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);
69435 if (result != MA_SUCCESS) {
69436 break;
69437 }
69438
69439 /* 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. */
69440 if (pFramesOut != NULL) {
69441 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);
69442 }
69443
69444 totalFramesProcessed += mappedFrameCount;
69445
69446 result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);
69447 if (result != MA_SUCCESS) {
69448 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. */
69449 }
69450 }
69451
69452 if (pFramesRead != NULL) {
69453 *pFramesRead = totalFramesProcessed;
69454 }
69455
69456 if (result == MA_SUCCESS && totalFramesProcessed == 0) {
69457 result = MA_AT_END;
69458 }
69459
69460 return result;
69461 }
69462
69463 MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex)
69464 {
69465 ma_job job;
69466 ma_result streamResult;
69467
69468 streamResult = ma_resource_manager_data_stream_result(pDataStream);
69469
69470 /* We cannot be using the data source after it's been uninitialized. */
69471 MA_ASSERT(streamResult != MA_UNAVAILABLE);
69472
69473 if (pDataStream == NULL) {
69474 return MA_INVALID_ARGS;
69475 }
69476
69477 if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
69478 return MA_INVALID_OPERATION;
69479 }
69480
69481 /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */
69482 if (c89atomic_load_32(&pDataStream->seekCounter) == 0) {
69483 if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) {
69484 return MA_SUCCESS;
69485 }
69486 }
69487
69488
69489 /* 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. */
69490 c89atomic_fetch_add_32(&pDataStream->seekCounter, 1);
69491
69492 /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */
69493 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);
69494
69495 /*
69496 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
69497 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
69498 the first page.
69499 */
69500 pDataStream->relativeCursor = 0;
69501 pDataStream->currentPageIndex = 0;
69502 c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);
69503 c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);
69504
69505 /* 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. */
69506 c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE);
69507
69508 /*
69509 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
69510 are invalid and any content contained within them will be discarded and replaced with newly decoded data.
69511 */
69512 job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM);
69513 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
69514 job.data.resourceManager.seekDataStream.pDataStream = pDataStream;
69515 job.data.resourceManager.seekDataStream.frameIndex = frameIndex;
69516 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
69517 }
69518
69519 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)
69520 {
69521 /* We cannot be using the data source after it's been uninitialized. */
69522 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69523
69524 if (pFormat != NULL) {
69525 *pFormat = ma_format_unknown;
69526 }
69527
69528 if (pChannels != NULL) {
69529 *pChannels = 0;
69530 }
69531
69532 if (pSampleRate != NULL) {
69533 *pSampleRate = 0;
69534 }
69535
69536 if (pChannelMap != NULL) {
69537 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
69538 }
69539
69540 if (pDataStream == NULL) {
69541 return MA_INVALID_ARGS;
69542 }
69543
69544 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
69545 return MA_INVALID_OPERATION;
69546 }
69547
69548 /*
69549 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
69550 such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.
69551 */
69552 return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
69553 }
69554
69555 MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor)
69556 {
69557 ma_result result;
69558
69559 if (pCursor == NULL) {
69560 return MA_INVALID_ARGS;
69561 }
69562
69563 *pCursor = 0;
69564
69565 /* We cannot be using the data source after it's been uninitialized. */
69566 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
69567
69568 if (pDataStream == NULL) {
69569 return MA_INVALID_ARGS;
69570 }
69571
69572 /*
69573 If the stream is in an erroneous state we need to return an invalid operation. We can allow
69574 this to be called when the data stream is in a busy state because the caller may have asked
69575 for an initial seek position and it's convenient to return that as the cursor position.
69576 */
69577 result = ma_resource_manager_data_stream_result(pDataStream);
69578 if (result != MA_SUCCESS && result != MA_BUSY) {
69579 return MA_INVALID_OPERATION;
69580 }
69581
69582 *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor);
69583
69584 return MA_SUCCESS;
69585 }
69586
69587 MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength)
69588 {
69589 ma_result streamResult;
69590
69591 if (pLength == NULL) {
69592 return MA_INVALID_ARGS;
69593 }
69594
69595 *pLength = 0;
69596
69597 streamResult = ma_resource_manager_data_stream_result(pDataStream);
69598
69599 /* We cannot be using the data source after it's been uninitialized. */
69600 MA_ASSERT(streamResult != MA_UNAVAILABLE);
69601
69602 if (pDataStream == NULL) {
69603 return MA_INVALID_ARGS;
69604 }
69605
69606 if (streamResult != MA_SUCCESS) {
69607 return streamResult;
69608 }
69609
69610 /*
69611 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
69612 calculated when we initialized it on the job thread.
69613 */
69614 *pLength = pDataStream->totalLengthInPCMFrames;
69615 if (*pLength == 0) {
69616 return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */
69617 }
69618
69619 return MA_SUCCESS;
69620 }
69621
69622 MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream)
69623 {
69624 if (pDataStream == NULL) {
69625 return MA_INVALID_ARGS;
69626 }
69627
69628 return (ma_result)c89atomic_load_i32(&pDataStream->result);
69629 }
69630
69631 MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping)
69632 {
69633 return ma_data_source_set_looping(pDataStream, isLooping);
69634 }
69635
69636 MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream)
69637 {
69638 if (pDataStream == NULL) {
69639 return MA_FALSE;
69640 }
69641
69642 return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */
69643 }
69644
69645 MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames)
69646 {
69647 ma_uint32 pageIndex0;
69648 ma_uint32 pageIndex1;
69649 ma_uint32 relativeCursor;
69650 ma_uint64 availableFrames;
69651
69652 if (pAvailableFrames == NULL) {
69653 return MA_INVALID_ARGS;
69654 }
69655
69656 *pAvailableFrames = 0;
69657
69658 if (pDataStream == NULL) {
69659 return MA_INVALID_ARGS;
69660 }
69661
69662 pageIndex0 = pDataStream->currentPageIndex;
69663 pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01;
69664 relativeCursor = pDataStream->relativeCursor;
69665
69666 availableFrames = 0;
69667 if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {
69668 availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;
69669 if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {
69670 availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);
69671 }
69672 }
69673
69674 *pAvailableFrames = availableFrames;
69675 return MA_SUCCESS;
69676 }
69677
69678
69679 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)
69680 {
69681 if (pDataSource == NULL) {
69682 return MA_INVALID_ARGS;
69683 }
69684
69685 MA_ZERO_OBJECT(pDataSource);
69686
69687 if (pConfig == NULL) {
69688 return MA_INVALID_ARGS;
69689 }
69690
69691 if (pResourceManager == NULL) {
69692 return MA_INVALID_ARGS;
69693 }
69694
69695 pDataSource->flags = pConfig->flags;
69696
69697 return MA_SUCCESS;
69698 }
69699
69700 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)
69701 {
69702 ma_result result;
69703
69704 result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource);
69705 if (result != MA_SUCCESS) {
69706 return result;
69707 }
69708
69709 /* The data source itself is just a data stream or a data buffer. */
69710 if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69711 return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream);
69712 } else {
69713 return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer);
69714 }
69715 }
69716
69717 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)
69718 {
69719 ma_resource_manager_data_source_config config;
69720
69721 config = ma_resource_manager_data_source_config_init();
69722 config.pFilePath = pName;
69723 config.flags = flags;
69724 config.pNotifications = pNotifications;
69725
69726 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
69727 }
69728
69729 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)
69730 {
69731 ma_resource_manager_data_source_config config;
69732
69733 config = ma_resource_manager_data_source_config_init();
69734 config.pFilePathW = pName;
69735 config.flags = flags;
69736 config.pNotifications = pNotifications;
69737
69738 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
69739 }
69740
69741 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)
69742 {
69743 ma_result result;
69744 ma_resource_manager_data_source_config config;
69745
69746 if (pExistingDataSource == NULL) {
69747 return MA_INVALID_ARGS;
69748 }
69749
69750 config = ma_resource_manager_data_source_config_init();
69751 config.flags = pExistingDataSource->flags;
69752
69753 result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource);
69754 if (result != MA_SUCCESS) {
69755 return result;
69756 }
69757
69758 /* Copying can only be done from data buffers. Streams cannot be copied. */
69759 if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69760 return MA_INVALID_OPERATION;
69761 }
69762
69763 return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer);
69764 }
69765
69766 MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource)
69767 {
69768 if (pDataSource == NULL) {
69769 return MA_INVALID_ARGS;
69770 }
69771
69772 /* All we need to is uninitialize the underlying data buffer or data stream. */
69773 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69774 return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream);
69775 } else {
69776 return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer);
69777 }
69778 }
69779
69780 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)
69781 {
69782 /* Safety. */
69783 if (pFramesRead != NULL) {
69784 *pFramesRead = 0;
69785 }
69786
69787 if (pDataSource == NULL) {
69788 return MA_INVALID_ARGS;
69789 }
69790
69791 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69792 return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead);
69793 } else {
69794 return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead);
69795 }
69796 }
69797
69798 MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex)
69799 {
69800 if (pDataSource == NULL) {
69801 return MA_INVALID_ARGS;
69802 }
69803
69804 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69805 return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex);
69806 } else {
69807 return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex);
69808 }
69809 }
69810
69811 MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
69812 {
69813 if (pDataSource == NULL) {
69814 return MA_INVALID_ARGS;
69815 }
69816
69817 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69818 return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount);
69819 } else {
69820 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
69821 }
69822 }
69823
69824 MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount)
69825 {
69826 if (pDataSource == NULL) {
69827 return MA_INVALID_ARGS;
69828 }
69829
69830 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69831 return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount);
69832 } else {
69833 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
69834 }
69835 }
69836
69837 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)
69838 {
69839 if (pDataSource == NULL) {
69840 return MA_INVALID_ARGS;
69841 }
69842
69843 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69844 return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
69845 } else {
69846 return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
69847 }
69848 }
69849
69850 MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor)
69851 {
69852 if (pDataSource == NULL) {
69853 return MA_INVALID_ARGS;
69854 }
69855
69856 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69857 return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor);
69858 } else {
69859 return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor);
69860 }
69861 }
69862
69863 MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength)
69864 {
69865 if (pDataSource == NULL) {
69866 return MA_INVALID_ARGS;
69867 }
69868
69869 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69870 return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength);
69871 } else {
69872 return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength);
69873 }
69874 }
69875
69876 MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource)
69877 {
69878 if (pDataSource == NULL) {
69879 return MA_INVALID_ARGS;
69880 }
69881
69882 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69883 return ma_resource_manager_data_stream_result(&pDataSource->backend.stream);
69884 } else {
69885 return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer);
69886 }
69887 }
69888
69889 MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping)
69890 {
69891 if (pDataSource == NULL) {
69892 return MA_INVALID_ARGS;
69893 }
69894
69895 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69896 return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping);
69897 } else {
69898 return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping);
69899 }
69900 }
69901
69902 MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource)
69903 {
69904 if (pDataSource == NULL) {
69905 return MA_FALSE;
69906 }
69907
69908 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69909 return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream);
69910 } else {
69911 return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer);
69912 }
69913 }
69914
69915 MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames)
69916 {
69917 if (pAvailableFrames == NULL) {
69918 return MA_INVALID_ARGS;
69919 }
69920
69921 *pAvailableFrames = 0;
69922
69923 if (pDataSource == NULL) {
69924 return MA_INVALID_ARGS;
69925 }
69926
69927 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
69928 return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames);
69929 } else {
69930 return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames);
69931 }
69932 }
69933
69934
69935 MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob)
69936 {
69937 if (pResourceManager == NULL) {
69938 return MA_INVALID_ARGS;
69939 }
69940
69941 return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
69942 }
69943
69944 MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager)
69945 {
69946 ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
69947 return ma_resource_manager_post_job(pResourceManager, &job);
69948 }
69949
69950 MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob)
69951 {
69952 if (pResourceManager == NULL) {
69953 return MA_INVALID_ARGS;
69954 }
69955
69956 return ma_job_queue_next(&pResourceManager->jobQueue, pJob);
69957 }
69958
69959
69960 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)
69961 {
69962 ma_result result = MA_SUCCESS;
69963 ma_resource_manager* pResourceManager;
69964 ma_resource_manager_data_buffer_node* pDataBufferNode;
69965
69966 MA_ASSERT(pJob != NULL);
69967
69968 pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager;
69969 MA_ASSERT(pResourceManager != NULL);
69970
69971 pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode;
69972 MA_ASSERT(pDataBufferNode != NULL);
69973 MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */
69974
69975 /* 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. */
69976 if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
69977 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. */
69978 }
69979
69980 /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */
69981 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {
69982 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */
69983 goto done;
69984 }
69985
69986 /*
69987 We're ready to start loading. Essentially what we're doing here is initializing the data supply
69988 of the node. Once this is complete, data buffers can have their connectors initialized which
69989 will allow then to have audio data read from them.
69990
69991 Note that when the data supply type has been moved away from "unknown", that is when other threads
69992 will determine that the node is available for data delivery and the data buffer connectors can be
69993 initialized. Therefore, it's important that it is set after the data supply has been initialized.
69994 */
69995 if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) {
69996 /*
69997 Decoding. This is the complex case because we're not going to be doing the entire decoding
69998 process here. Instead it's going to be split of multiple jobs and loaded in pages. The
69999 reason for this is to evenly distribute decoding time across multiple sounds, rather than
70000 having one huge sound hog all the available processing resources.
70001
70002 The first thing we do is initialize a decoder. This is allocated on the heap and is passed
70003 around to the paging jobs. When the last paging job has completed it's processing, it'll
70004 free the decoder for us.
70005
70006 This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job
70007 which is where the actual decoding work will be done. However, once this job is complete,
70008 the node will be in a state where data buffer connectors can be initialized.
70009 */
70010 ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */
70011 ma_job pageDataBufferNodeJob;
70012
70013 /* Allocate the decoder by initializing a decoded data supply. */
70014 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);
70015
70016 /*
70017 Don't ever propagate an MA_BUSY result code or else the resource manager will think the
70018 node is just busy decoding rather than in an error state. This should never happen, but
70019 including this logic for safety just in case.
70020 */
70021 if (result == MA_BUSY) {
70022 result = MA_ERROR;
70023 }
70024
70025 if (result != MA_SUCCESS) {
70026 if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) {
70027 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));
70028 } else {
70029 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
70030 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));
70031 #endif
70032 }
70033
70034 goto done;
70035 }
70036
70037 /*
70038 At this point the node's data supply is initialized and other threads can start initializing
70039 their data buffer connectors. However, no data will actually be available until we start to
70040 actually decode it. To do this, we need to post a paging job which is where the decoding
70041 work is done.
70042
70043 Note that if an error occurred at an earlier point, this section will have been skipped.
70044 */
70045 pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE);
70046 pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
70047 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager;
70048 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode;
70049 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder;
70050 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification;
70051 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence;
70052
70053 /* The job has been set up so it can now be posted. */
70054 result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
70055
70056 /*
70057 When we get here, we want to make sure the result code is set to MA_BUSY. The reason for
70058 this is that the result will be copied over to the node's internal result variable. In
70059 this case, since the decoding is still in-progress, we need to make sure the result code
70060 is set to MA_BUSY.
70061 */
70062 if (result != MA_SUCCESS) {
70063 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));
70064 ma_decoder_uninit(pDecoder);
70065 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
70066 } else {
70067 result = MA_BUSY;
70068 }
70069 } else {
70070 /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */
70071 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);
70072 }
70073
70074
70075 done:
70076 /* File paths are no longer needed. */
70077 ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks);
70078 ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks);
70079
70080 /*
70081 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
70082 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
70083 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
70084 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
70085 other error code would cause the buffer to look like it's in a state that it's not.
70086 */
70087 c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
70088
70089 /* At this point initialization is complete and we can signal the notification if any. */
70090 if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) {
70091 ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification);
70092 }
70093 if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) {
70094 ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence);
70095 }
70096
70097 /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */
70098 if (result != MA_BUSY) {
70099 if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) {
70100 ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification);
70101 }
70102 if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) {
70103 ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence);
70104 }
70105 }
70106
70107 /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */
70108 c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
70109
70110 /* A busy result should be considered successful from the point of view of the job system. */
70111 if (result == MA_BUSY) {
70112 result = MA_SUCCESS;
70113 }
70114
70115 return result;
70116 }
70117
70118 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob)
70119 {
70120 ma_resource_manager* pResourceManager;
70121 ma_resource_manager_data_buffer_node* pDataBufferNode;
70122
70123 MA_ASSERT(pJob != NULL);
70124
70125 pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager;
70126 MA_ASSERT(pResourceManager != NULL);
70127
70128 pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode;
70129 MA_ASSERT(pDataBufferNode != NULL);
70130
70131 if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
70132 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70133 }
70134
70135 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
70136
70137 /* The event needs to be signalled last. */
70138 if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) {
70139 ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification);
70140 }
70141
70142 if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) {
70143 ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence);
70144 }
70145
70146 c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
70147 return MA_SUCCESS;
70148 }
70149
70150 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob)
70151 {
70152 ma_result result = MA_SUCCESS;
70153 ma_resource_manager* pResourceManager;
70154 ma_resource_manager_data_buffer_node* pDataBufferNode;
70155
70156 MA_ASSERT(pJob != NULL);
70157
70158 pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager;
70159 MA_ASSERT(pResourceManager != NULL);
70160
70161 pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode;
70162 MA_ASSERT(pDataBufferNode != NULL);
70163
70164 if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
70165 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70166 }
70167
70168 /* Don't do any more decoding if the data buffer has started the uninitialization process. */
70169 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);
70170 if (result != MA_BUSY) {
70171 goto done;
70172 }
70173
70174 /* We're ready to decode the next page. */
70175 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
70176
70177 /*
70178 If we have a success code by this point, we want to post another job. We're going to set the
70179 result back to MA_BUSY to make it clear that there's still more to load.
70180 */
70181 if (result == MA_SUCCESS) {
70182 ma_job newJob;
70183 newJob = *pJob; /* Everything is the same as the input job, except the execution order. */
70184 newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */
70185
70186 result = ma_resource_manager_post_job(pResourceManager, &newJob);
70187
70188 /* Since the sound isn't yet fully decoded we want the status to be set to busy. */
70189 if (result == MA_SUCCESS) {
70190 result = MA_BUSY;
70191 }
70192 }
70193
70194 done:
70195 /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */
70196 if (result != MA_BUSY) {
70197 ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
70198 ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks);
70199 }
70200
70201 /* If we reached the end we need to treat it as successful. */
70202 if (result == MA_AT_END) {
70203 result = MA_SUCCESS;
70204 }
70205
70206 /* Make sure we set the result of node in case some error occurred. */
70207 c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
70208
70209 /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */
70210 if (result != MA_BUSY) {
70211 if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) {
70212 ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification);
70213 }
70214
70215 if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) {
70216 ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence);
70217 }
70218 }
70219
70220 c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
70221 return result;
70222 }
70223
70224
70225 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)
70226 {
70227 ma_result result = MA_SUCCESS;
70228 ma_resource_manager* pResourceManager;
70229 ma_resource_manager_data_buffer* pDataBuffer;
70230 ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown;
70231 ma_bool32 isConnectorInitialized = MA_FALSE;
70232
70233 /*
70234 All we're doing here is checking if the node has finished loading. If not, we just re-post the job
70235 and keep waiting. Otherwise we increment the execution counter and set the buffer's result code.
70236 */
70237 MA_ASSERT(pJob != NULL);
70238
70239 pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer;
70240 MA_ASSERT(pDataBuffer != NULL);
70241
70242 pResourceManager = pDataBuffer->pResourceManager;
70243
70244 if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
70245 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. */
70246 }
70247
70248 /*
70249 First thing we need to do is check whether or not the data buffer is getting deleted. If so we
70250 just abort, but making sure we increment the execution pointer.
70251 */
70252 result = ma_resource_manager_data_buffer_result(pDataBuffer);
70253 if (result != MA_BUSY) {
70254 goto done; /* <-- This will ensure the exucution pointer is incremented. */
70255 } else {
70256 result = MA_SUCCESS; /* <-- Make sure this is reset. */
70257 }
70258
70259 /* Try initializing the connector if we haven't already. */
70260 isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer);
70261 if (isConnectorInitialized == MA_FALSE) {
70262 dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);
70263
70264 if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {
70265 /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
70266 ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */
70267 dataSourceConfig = ma_resource_manager_data_source_config_init();
70268 dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames;
70269 dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames;
70270 dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames;
70271 dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames;
70272 dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping;
70273
70274 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
70275 if (result != MA_SUCCESS) {
70276 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));
70277 goto done;
70278 }
70279 } else {
70280 /* 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. */
70281 }
70282 } else {
70283 /* The connector is already initialized. Nothing to do here. */
70284 }
70285
70286 /*
70287 If the data node is still loading, we need to repost the job and *not* increment the execution
70288 pointer (i.e. we need to not fall through to the "done" label).
70289
70290 There is a hole between here and the where the data connector is initialized where the data
70291 buffer node may have finished initializing. We need to check for this by checking the result of
70292 the data buffer node and whether or not we had an unknown data supply type at the time of
70293 trying to initialize the data connector.
70294 */
70295 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
70296 if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {
70297 return ma_resource_manager_post_job(pResourceManager, pJob);
70298 }
70299
70300 done:
70301 /* Only move away from a busy code so that we don't trash any existing error codes. */
70302 c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);
70303
70304 /* Only signal the other threads after the result has been set just for cleanliness sake. */
70305 if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) {
70306 ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification);
70307 }
70308 if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) {
70309 ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence);
70310 }
70311
70312 /*
70313 If at this point the data buffer has not had it's connector initialized, it means the
70314 notification event was never signalled which means we need to signal it here.
70315 */
70316 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) {
70317 if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) {
70318 ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification);
70319 }
70320 if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) {
70321 ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence);
70322 }
70323 }
70324
70325 c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
70326 return result;
70327 }
70328
70329 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)
70330 {
70331 ma_resource_manager* pResourceManager;
70332 ma_resource_manager_data_buffer* pDataBuffer;
70333
70334 MA_ASSERT(pJob != NULL);
70335
70336 pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer;
70337 MA_ASSERT(pDataBuffer != NULL);
70338
70339 pResourceManager = pDataBuffer->pResourceManager;
70340
70341 if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
70342 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70343 }
70344
70345 ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
70346
70347 /* The event needs to be signalled last. */
70348 if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) {
70349 ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification);
70350 }
70351
70352 if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) {
70353 ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence);
70354 }
70355
70356 c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
70357 return MA_SUCCESS;
70358 }
70359
70360 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)
70361 {
70362 ma_result result = MA_SUCCESS;
70363 ma_decoder_config decoderConfig;
70364 ma_uint32 pageBufferSizeInBytes;
70365 ma_resource_manager* pResourceManager;
70366 ma_resource_manager_data_stream* pDataStream;
70367
70368 MA_ASSERT(pJob != NULL);
70369
70370 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream;
70371 MA_ASSERT(pDataStream != NULL);
70372
70373 pResourceManager = pDataStream->pResourceManager;
70374
70375 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
70376 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70377 }
70378
70379 if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {
70380 result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */
70381 goto done;
70382 }
70383
70384 /* We need to initialize the decoder first so we can determine the size of the pages. */
70385 decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager);
70386
70387 if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) {
70388 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);
70389 } else {
70390 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);
70391 }
70392 if (result != MA_SUCCESS) {
70393 goto done;
70394 }
70395
70396 /* Retrieve the total length of the file before marking the decoder as loaded. */
70397 if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
70398 result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
70399 if (result != MA_SUCCESS) {
70400 goto done; /* Failed to retrieve the length. */
70401 }
70402 } else {
70403 pDataStream->totalLengthInPCMFrames = 0;
70404 }
70405
70406 /*
70407 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
70408 and we don't want to have another thread trying to access the decoder while it's scanning.
70409 */
70410 pDataStream->isDecoderInitialized = MA_TRUE;
70411
70412 /* We have the decoder so we can now initialize our page buffer. */
70413 pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);
70414
70415 pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks);
70416 if (pDataStream->pPageData == NULL) {
70417 ma_decoder_uninit(&pDataStream->decoder);
70418 result = MA_OUT_OF_MEMORY;
70419 goto done;
70420 }
70421
70422 /* Seek to our initial seek point before filling the initial pages. */
70423 ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint);
70424
70425 /* We have our decoder and our page buffer, so now we need to fill our pages. */
70426 ma_resource_manager_data_stream_fill_pages(pDataStream);
70427
70428 /* And now we're done. We want to make sure the result is MA_SUCCESS. */
70429 result = MA_SUCCESS;
70430
70431 done:
70432 ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks);
70433 ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks);
70434
70435 /* 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). */
70436 c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result);
70437
70438 /* Only signal the other threads after the result has been set just for cleanliness sake. */
70439 if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) {
70440 ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification);
70441 }
70442 if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) {
70443 ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence);
70444 }
70445
70446 c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
70447 return result;
70448 }
70449
70450 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)
70451 {
70452 ma_resource_manager* pResourceManager;
70453 ma_resource_manager_data_stream* pDataStream;
70454
70455 MA_ASSERT(pJob != NULL);
70456
70457 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream;
70458 MA_ASSERT(pDataStream != NULL);
70459
70460 pResourceManager = pDataStream->pResourceManager;
70461
70462 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
70463 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70464 }
70465
70466 /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */
70467 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE);
70468
70469 if (pDataStream->isDecoderInitialized) {
70470 ma_decoder_uninit(&pDataStream->decoder);
70471 }
70472
70473 if (pDataStream->pPageData != NULL) {
70474 ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks);
70475 pDataStream->pPageData = NULL; /* Just in case... */
70476 }
70477
70478 ma_data_source_uninit(&pDataStream->ds);
70479
70480 /* The event needs to be signalled last. */
70481 if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) {
70482 ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification);
70483 }
70484 if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) {
70485 ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence);
70486 }
70487
70488 /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/
70489 return MA_SUCCESS;
70490 }
70491
70492 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)
70493 {
70494 ma_result result = MA_SUCCESS;
70495 ma_resource_manager* pResourceManager;
70496 ma_resource_manager_data_stream* pDataStream;
70497
70498 MA_ASSERT(pJob != NULL);
70499
70500 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream;
70501 MA_ASSERT(pDataStream != NULL);
70502
70503 pResourceManager = pDataStream->pResourceManager;
70504
70505 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
70506 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70507 }
70508
70509 /* For streams, the status should be MA_SUCCESS. */
70510 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
70511 result = MA_INVALID_OPERATION;
70512 goto done;
70513 }
70514
70515 ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex);
70516
70517 done:
70518 c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
70519 return result;
70520 }
70521
70522 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob)
70523 {
70524 ma_result result = MA_SUCCESS;
70525 ma_resource_manager* pResourceManager;
70526 ma_resource_manager_data_stream* pDataStream;
70527
70528 MA_ASSERT(pJob != NULL);
70529
70530 pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream;
70531 MA_ASSERT(pDataStream != NULL);
70532
70533 pResourceManager = pDataStream->pResourceManager;
70534
70535 if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) {
70536 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
70537 }
70538
70539 /* For streams the status should be MA_SUCCESS for this to do anything. */
70540 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) {
70541 result = MA_INVALID_OPERATION;
70542 goto done;
70543 }
70544
70545 /*
70546 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
70547 instead of initializing the decoder, we seek to a frame.
70548 */
70549 ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex);
70550
70551 /* After seeking we'll need to reload the pages. */
70552 ma_resource_manager_data_stream_fill_pages(pDataStream);
70553
70554 /* We need to let the public API know that we're done seeking. */
70555 c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1);
70556
70557 done:
70558 c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
70559 return result;
70560 }
70561
70562 MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob)
70563 {
70564 if (pResourceManager == NULL || pJob == NULL) {
70565 return MA_INVALID_ARGS;
70566 }
70567
70568 return ma_job_process(pJob);
70569 }
70570
70571 MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager)
70572 {
70573 ma_result result;
70574 ma_job job;
70575
70576 if (pResourceManager == NULL) {
70577 return MA_INVALID_ARGS;
70578 }
70579
70580 /* This will return MA_CANCELLED if the next job is a quit job. */
70581 result = ma_resource_manager_next_job(pResourceManager, &job);
70582 if (result != MA_SUCCESS) {
70583 return result;
70584 }
70585
70586 return ma_job_process(&job);
70587 }
70588 #else
70589 /* 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. */
70590 static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
70591 static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
70592 static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
70593 static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
70594 static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
70595 static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
70596 static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
70597 static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
70598 static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
70599 #endif /* MA_NO_RESOURCE_MANAGER */
70600
70601
70602 #ifndef MA_NO_NODE_GRAPH
70603 /* 10ms @ 48K = 480. Must never exceed 65535. */
70604 #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
70605 #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
70606 #endif
70607
70608
70609 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);
70610
70611 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)
70612 {
70613 #ifndef MA_NO_GENERATION
70614 {
70615 ma_waveform_config waveformConfig;
70616 ma_waveform waveform;
70617
70618 waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);
70619 ma_waveform_init(&waveformConfig, &waveform);
70620 ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);
70621 }
70622 #else
70623 {
70624 (void)pFramesOut;
70625 (void)frameCount;
70626 (void)format;
70627 (void)channels;
70628 (void)sampleRate;
70629 #if defined(MA_DEBUG_OUTPUT)
70630 {
70631 #if _MSC_VER
70632 #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.")
70633 #endif
70634 }
70635 #endif
70636 }
70637 #endif
70638 }
70639
70640
70641
70642 MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
70643 {
70644 ma_node_graph_config config;
70645
70646 MA_ZERO_OBJECT(&config);
70647 config.channels = channels;
70648 config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
70649
70650 return config;
70651 }
70652
70653
70654 static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading)
70655 {
70656 MA_ASSERT(pNodeGraph != NULL);
70657 c89atomic_exchange_32(&pNodeGraph->isReading, isReading);
70658 }
70659
70660 #if 0
70661 static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph)
70662 {
70663 MA_ASSERT(pNodeGraph != NULL);
70664 return c89atomic_load_32(&pNodeGraph->isReading);
70665 }
70666 #endif
70667
70668
70669 static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70670 {
70671 ma_node_graph* pNodeGraph = (ma_node_graph*)pNode;
70672 ma_uint64 framesRead;
70673
70674 ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead);
70675
70676 *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */
70677
70678 (void)ppFramesIn;
70679 (void)pFrameCountIn;
70680 }
70681
70682 static ma_node_vtable g_node_graph_node_vtable =
70683 {
70684 ma_node_graph_node_process_pcm_frames,
70685 NULL, /* onGetRequiredInputFrameCount */
70686 0, /* 0 input buses. */
70687 1, /* 1 output bus. */
70688 0 /* Flags. */
70689 };
70690
70691 static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
70692 {
70693 MA_ASSERT(pNode != NULL);
70694 MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1);
70695 MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1);
70696
70697 /* Input channel count needs to be the same as the output channel count. */
70698 MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0));
70699
70700 /* We don't need to do anything here because it's a passthrough. */
70701 (void)pNode;
70702 (void)ppFramesIn;
70703 (void)pFrameCountIn;
70704 (void)ppFramesOut;
70705 (void)pFrameCountOut;
70706
70707 #if 0
70708 /* The data has already been mixed. We just need to move it to the output buffer. */
70709 if (ppFramesIn != NULL) {
70710 ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0));
70711 }
70712 #endif
70713 }
70714
70715 static ma_node_vtable g_node_graph_endpoint_vtable =
70716 {
70717 ma_node_graph_endpoint_process_pcm_frames,
70718 NULL, /* onGetRequiredInputFrameCount */
70719 1, /* 1 input bus. */
70720 1, /* 1 output bus. */
70721 MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */
70722 };
70723
70724 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph)
70725 {
70726 ma_result result;
70727 ma_node_config baseConfig;
70728 ma_node_config endpointConfig;
70729
70730 if (pNodeGraph == NULL) {
70731 return MA_INVALID_ARGS;
70732 }
70733
70734 MA_ZERO_OBJECT(pNodeGraph);
70735 pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames;
70736 if (pNodeGraph->nodeCacheCapInFrames == 0) {
70737 pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
70738 }
70739
70740
70741 /* Base node so we can use the node graph as a node into another graph. */
70742 baseConfig = ma_node_config_init();
70743 baseConfig.vtable = &g_node_graph_node_vtable;
70744 baseConfig.pOutputChannels = &pConfig->channels;
70745
70746 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base);
70747 if (result != MA_SUCCESS) {
70748 return result;
70749 }
70750
70751
70752 /* Endpoint. */
70753 endpointConfig = ma_node_config_init();
70754 endpointConfig.vtable = &g_node_graph_endpoint_vtable;
70755 endpointConfig.pInputChannels = &pConfig->channels;
70756 endpointConfig.pOutputChannels = &pConfig->channels;
70757
70758 result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint);
70759 if (result != MA_SUCCESS) {
70760 ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
70761 return result;
70762 }
70763
70764 return MA_SUCCESS;
70765 }
70766
70767 MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks)
70768 {
70769 if (pNodeGraph == NULL) {
70770 return;
70771 }
70772
70773 ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
70774 }
70775
70776 MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph)
70777 {
70778 if (pNodeGraph == NULL) {
70779 return NULL;
70780 }
70781
70782 return &pNodeGraph->endpoint;
70783 }
70784
70785 MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
70786 {
70787 ma_result result = MA_SUCCESS;
70788 ma_uint64 totalFramesRead;
70789 ma_uint32 channels;
70790
70791 if (pFramesRead != NULL) {
70792 *pFramesRead = 0; /* Safety. */
70793 }
70794
70795 if (pNodeGraph == NULL) {
70796 return MA_INVALID_ARGS;
70797 }
70798
70799 channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
70800
70801
70802 /* We'll be nice and try to do a full read of all frameCount frames. */
70803 totalFramesRead = 0;
70804 while (totalFramesRead < frameCount) {
70805 ma_uint32 framesJustRead;
70806 ma_uint64 framesToRead = frameCount - totalFramesRead;
70807
70808 if (framesToRead > 0xFFFFFFFF) {
70809 framesToRead = 0xFFFFFFFF;
70810 }
70811
70812 ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
70813 {
70814 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));
70815 }
70816 ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
70817
70818 totalFramesRead += framesJustRead;
70819
70820 if (result != MA_SUCCESS) {
70821 break;
70822 }
70823
70824 /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
70825 if (framesJustRead == 0) {
70826 break;
70827 }
70828 }
70829
70830 /* 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. */
70831 if (totalFramesRead < frameCount) {
70832 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);
70833 }
70834
70835 if (pFramesRead != NULL) {
70836 *pFramesRead = totalFramesRead;
70837 }
70838
70839 return result;
70840 }
70841
70842 MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph)
70843 {
70844 if (pNodeGraph == NULL) {
70845 return 0;
70846 }
70847
70848 return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
70849 }
70850
70851 MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph)
70852 {
70853 if (pNodeGraph == NULL) {
70854 return 0;
70855 }
70856
70857 return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */
70858 }
70859
70860 MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime)
70861 {
70862 if (pNodeGraph == NULL) {
70863 return MA_INVALID_ARGS;
70864 }
70865
70866 return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */
70867 }
70868
70869
70870 #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. */
70871
70872 static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)
70873 {
70874 MA_ASSERT(pOutputBus != NULL);
70875 MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);
70876 MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));
70877 MA_ASSERT(channels < 256);
70878
70879 MA_ZERO_OBJECT(pOutputBus);
70880
70881 if (channels == 0) {
70882 return MA_INVALID_ARGS;
70883 }
70884
70885 pOutputBus->pNode = pNode;
70886 pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;
70887 pOutputBus->channels = (ma_uint8)channels;
70888 pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */
70889 pOutputBus->volume = 1;
70890
70891 return MA_SUCCESS;
70892 }
70893
70894 static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)
70895 {
70896 ma_spinlock_lock(&pOutputBus->lock);
70897 }
70898
70899 static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)
70900 {
70901 ma_spinlock_unlock(&pOutputBus->lock);
70902 }
70903
70904
70905 static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)
70906 {
70907 return pOutputBus->channels;
70908 }
70909
70910
70911 static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)
70912 {
70913 if (hasRead) {
70914 c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
70915 } else {
70916 c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
70917 }
70918 }
70919
70920 static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)
70921 {
70922 return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;
70923 }
70924
70925
70926 static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached)
70927 {
70928 c89atomic_exchange_32(&pOutputBus->isAttached, isAttached);
70929 }
70930
70931 static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)
70932 {
70933 return c89atomic_load_32(&pOutputBus->isAttached);
70934 }
70935
70936
70937 static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)
70938 {
70939 MA_ASSERT(pOutputBus != NULL);
70940
70941 if (volume < 0.0f) {
70942 volume = 0.0f;
70943 }
70944
70945 c89atomic_exchange_f32(&pOutputBus->volume, volume);
70946
70947 return MA_SUCCESS;
70948 }
70949
70950 static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)
70951 {
70952 return c89atomic_load_f32((float*)&pOutputBus->volume);
70953 }
70954
70955
70956 static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
70957 {
70958 MA_ASSERT(pInputBus != NULL);
70959 MA_ASSERT(channels < 256);
70960
70961 MA_ZERO_OBJECT(pInputBus);
70962
70963 if (channels == 0) {
70964 return MA_INVALID_ARGS;
70965 }
70966
70967 pInputBus->channels = (ma_uint8)channels;
70968
70969 return MA_SUCCESS;
70970 }
70971
70972 static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
70973 {
70974 MA_ASSERT(pInputBus != NULL);
70975
70976 ma_spinlock_lock(&pInputBus->lock);
70977 }
70978
70979 static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
70980 {
70981 MA_ASSERT(pInputBus != NULL);
70982
70983 ma_spinlock_unlock(&pInputBus->lock);
70984 }
70985
70986
70987 static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)
70988 {
70989 c89atomic_fetch_add_32(&pInputBus->nextCounter, 1);
70990 }
70991
70992 static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)
70993 {
70994 c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1);
70995 }
70996
70997 static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)
70998 {
70999 return c89atomic_load_32(&pInputBus->nextCounter);
71000 }
71001
71002
71003 static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)
71004 {
71005 return pInputBus->channels;
71006 }
71007
71008
71009 static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
71010 {
71011 MA_ASSERT(pInputBus != NULL);
71012 MA_ASSERT(pOutputBus != NULL);
71013
71014 /*
71015 Mark the output bus as detached first. This will prevent future iterations on the audio thread
71016 from iterating this output bus.
71017 */
71018 ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);
71019
71020 /*
71021 We cannot use the output bus lock here since it'll be getting used at a higher level, but we do
71022 still need to use the input bus lock since we'll be updating pointers on two different output
71023 buses. The same rules apply here as the attaching case. Although we're using a lock here, we're
71024 *not* using a lock when iterating over the list in the audio thread. We therefore need to craft
71025 this in a way such that the iteration on the audio thread doesn't break.
71026
71027 The the first thing to do is swap out the "next" pointer of the previous output bus with the
71028 new "next" output bus. This is the operation that matters for iteration on the audio thread.
71029 After that, the previous pointer on the new "next" pointer needs to be updated, after which
71030 point the linked list will be in a good state.
71031 */
71032 ma_node_input_bus_lock(pInputBus);
71033 {
71034 ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev);
71035 ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext);
71036
71037 if (pOldPrev != NULL) {
71038 c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */
71039 }
71040 if (pOldNext != NULL) {
71041 c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */
71042 }
71043 }
71044 ma_node_input_bus_unlock(pInputBus);
71045
71046 /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */
71047 c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */
71048 c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */
71049 pOutputBus->pInputNode = NULL;
71050 pOutputBus->inputNodeInputBusIndex = 0;
71051
71052
71053 /*
71054 For thread-safety reasons, we don't want to be returning from this straight away. We need to
71055 wait for the audio thread to finish with the output bus. There's two things we need to wait
71056 for. The first is the part that selects the next output bus in the list, and the other is the
71057 part that reads from the output bus. Basically all we're doing is waiting for the input bus
71058 to stop referencing the output bus.
71059
71060 We're doing this part last because we want the section above to run while the audio thread
71061 is finishing up with the output bus, just for efficiency reasons. We marked the output bus as
71062 detached right at the top of this function which is going to prevent the audio thread from
71063 iterating the output bus again.
71064 */
71065
71066 /* Part 1: Wait for the current iteration to complete. */
71067 while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {
71068 ma_yield();
71069 }
71070
71071 /* Part 2: Wait for any reads to complete. */
71072 while (c89atomic_load_32(&pOutputBus->refCount) > 0) {
71073 ma_yield();
71074 }
71075
71076 /*
71077 At this point we're done detaching and we can be guaranteed that the audio thread is not going
71078 to attempt to reference this output bus again (until attached again).
71079 */
71080 }
71081
71082 #if 0 /* Not used at the moment, but leaving here in case I need it later. */
71083 static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
71084 {
71085 MA_ASSERT(pInputBus != NULL);
71086 MA_ASSERT(pOutputBus != NULL);
71087
71088 ma_node_output_bus_lock(pOutputBus);
71089 {
71090 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
71091 }
71092 ma_node_output_bus_unlock(pOutputBus);
71093 }
71094 #endif
71095
71096 static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)
71097 {
71098 MA_ASSERT(pInputBus != NULL);
71099 MA_ASSERT(pOutputBus != NULL);
71100
71101 ma_node_output_bus_lock(pOutputBus);
71102 {
71103 ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode);
71104
71105 /* Detach from any existing attachment first if necessary. */
71106 if (pOldInputNode != NULL) {
71107 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
71108 }
71109
71110 /*
71111 At this point we can be sure the output bus is not attached to anything. The linked list in the
71112 old input bus has been updated so that pOutputBus will not get iterated again.
71113 */
71114 pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */
71115 pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex;
71116
71117 /*
71118 Now we need to attach the output bus to the linked list. This involves updating two pointers on
71119 two different output buses so I'm going to go ahead and keep this simple and just use a lock.
71120 There are ways to do this without a lock, but it's just too hard to maintain for it's value.
71121
71122 Although we're locking here, it's important to remember that we're *not* locking when iterating
71123 and reading audio data since that'll be running on the audio thread. As a result we need to be
71124 careful how we craft this so that we don't break iteration. What we're going to do is always
71125 attach the new item so that it becomes the first item in the list. That way, as we're iterating
71126 we won't break any links in the list and iteration will continue safely. The detaching case will
71127 also be crafted in a way as to not break list iteration. It's important to remember to use
71128 atomic exchanges here since no locking is happening on the audio thread during iteration.
71129 */
71130 ma_node_input_bus_lock(pInputBus);
71131 {
71132 ma_node_output_bus* pNewPrev = &pInputBus->head;
71133 ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext);
71134
71135 /* Update the local output bus. */
71136 c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);
71137 c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);
71138
71139 /* Update the other output buses to point back to the local output bus. */
71140 c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */
71141
71142 /* Do the previous pointer last. This is only used for detachment. */
71143 if (pNewNext != NULL) {
71144 c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus);
71145 }
71146 }
71147 ma_node_input_bus_unlock(pInputBus);
71148
71149 /*
71150 Mark the node as attached last. This is used to controlling whether or the output bus will be
71151 iterated on the audio thread. Mainly required for detachment purposes.
71152 */
71153 ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);
71154 }
71155 ma_node_output_bus_unlock(pOutputBus);
71156 }
71157
71158 static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
71159 {
71160 ma_node_output_bus* pNext;
71161
71162 MA_ASSERT(pInputBus != NULL);
71163
71164 if (pOutputBus == NULL) {
71165 return NULL;
71166 }
71167
71168 ma_node_input_bus_next_begin(pInputBus);
71169 {
71170 pNext = pOutputBus;
71171 for (;;) {
71172 pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext);
71173 if (pNext == NULL) {
71174 break; /* Reached the end. */
71175 }
71176
71177 if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {
71178 continue; /* The node is not attached. Keep checking. */
71179 }
71180
71181 /* The next node has been selected. */
71182 break;
71183 }
71184
71185 /* We need to increment the reference count of the selected node. */
71186 if (pNext != NULL) {
71187 c89atomic_fetch_add_32(&pNext->refCount, 1);
71188 }
71189
71190 /* The previous node is no longer being referenced. */
71191 c89atomic_fetch_sub_32(&pOutputBus->refCount, 1);
71192 }
71193 ma_node_input_bus_next_end(pInputBus);
71194
71195 return pNext;
71196 }
71197
71198 static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
71199 {
71200 return ma_node_input_bus_next(pInputBus, &pInputBus->head);
71201 }
71202
71203
71204
71205 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)
71206 {
71207 ma_result result = MA_SUCCESS;
71208 ma_node_output_bus* pOutputBus;
71209 ma_node_output_bus* pFirst;
71210 ma_uint32 inputChannels;
71211 ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
71212
71213 (void)pInputNode; /* Not currently used. */
71214
71215 /*
71216 This will be called from the audio thread which means we can't be doing any locking. Basically,
71217 this function will not perfom any locking, whereas attaching and detaching will, but crafted in
71218 such a way that we don't need to perform any locking here. The important thing to remember is
71219 to always iterate in a forward direction.
71220
71221 In order to process any data we need to first read from all input buses. That's where this
71222 function comes in. This iterates over each of the attachments and accumulates/mixes them. We
71223 also convert the channels to the nodes output channel count before mixing. We want to do this
71224 channel conversion so that the caller of this function can invoke the processing callback
71225 without having to do it themselves.
71226
71227 When we iterate over each of the attachments on the input bus, we need to read as much data as
71228 we can from each of them so that we don't end up with holes between each of the attachments. To
71229 do this, we need to read from each attachment in a loop and read as many frames as we can, up
71230 to `frameCount`.
71231 */
71232 MA_ASSERT(pInputNode != NULL);
71233 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. */
71234
71235 *pFramesRead = 0; /* Safety. */
71236
71237 inputChannels = ma_node_input_bus_get_channels(pInputBus);
71238
71239 /*
71240 We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They
71241 are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()
71242 once per iteration, however we have an optimization to checks whether or not it's the first item in
71243 the list. We therefore need to store a pointer to the first item rather than repeatedly calling
71244 ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it
71245 after calling ma_node_input_bus_next(), which we won't be.
71246 */
71247 pFirst = ma_node_input_bus_first(pInputBus);
71248 if (pFirst == NULL) {
71249 return MA_SUCCESS; /* No attachments. Read nothing. */
71250 }
71251
71252 for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
71253 ma_uint32 framesProcessed = 0;
71254 ma_bool32 isSilentOutput = MA_FALSE;
71255
71256 MA_ASSERT(pOutputBus->pNode != NULL);
71257 MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL);
71258
71259 isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;
71260
71261 if (pFramesOut != NULL) {
71262 /* Read. */
71263 float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
71264 ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;
71265
71266 while (framesProcessed < frameCount) {
71267 float* pRunningFramesOut;
71268 ma_uint32 framesToRead;
71269 ma_uint32 framesJustRead;
71270
71271 framesToRead = frameCount - framesProcessed;
71272 if (framesToRead > tempCapInFrames) {
71273 framesToRead = tempCapInFrames;
71274 }
71275
71276 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
71277
71278 if (doesOutputBufferHaveContent == MA_FALSE) {
71279 /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */
71280 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
71281 } else {
71282 /* Slow path. Not the first attachment. Mixing required. */
71283 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);
71284 if (result == MA_SUCCESS || result == MA_AT_END) {
71285 if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */
71286 ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);
71287 }
71288 }
71289 }
71290
71291 framesProcessed += framesJustRead;
71292
71293 /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */
71294 if (result != MA_SUCCESS) {
71295 break;
71296 }
71297
71298 /* If we didn't read anything, abort so we don't get stuck in a loop. */
71299 if (framesJustRead == 0) {
71300 break;
71301 }
71302 }
71303
71304 /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */
71305 if (pOutputBus == pFirst && framesProcessed < frameCount) {
71306 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);
71307 }
71308
71309 if (isSilentOutput == MA_FALSE) {
71310 doesOutputBufferHaveContent = MA_TRUE;
71311 }
71312 } else {
71313 /* Seek. */
71314 ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);
71315 }
71316 }
71317
71318 /* If we didn't output anything, output silence. */
71319 if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) {
71320 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels);
71321 }
71322
71323 /* In this path we always "process" the entire amount. */
71324 *pFramesRead = frameCount;
71325
71326 return result;
71327 }
71328
71329
71330 MA_API ma_node_config ma_node_config_init(void)
71331 {
71332 ma_node_config config;
71333
71334 MA_ZERO_OBJECT(&config);
71335 config.initialState = ma_node_state_started; /* Nodes are started by default. */
71336 config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
71337 config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
71338
71339 return config;
71340 }
71341
71342
71343
71344 static ma_result ma_node_detach_full(ma_node* pNode);
71345
71346 static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex)
71347 {
71348 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71349 ma_uint32 iInputBus;
71350 float* pBasePtr;
71351
71352 MA_ASSERT(pNodeBase != NULL);
71353
71354 /* Input data is stored at the front of the buffer. */
71355 pBasePtr = pNodeBase->pCachedData;
71356 for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) {
71357 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
71358 }
71359
71360 return pBasePtr;
71361 }
71362
71363 static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex)
71364 {
71365 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71366 ma_uint32 iInputBus;
71367 ma_uint32 iOutputBus;
71368 float* pBasePtr;
71369
71370 MA_ASSERT(pNodeBase != NULL);
71371
71372 /* Cached output data starts after the input data. */
71373 pBasePtr = pNodeBase->pCachedData;
71374 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
71375 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
71376 }
71377
71378 for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) {
71379 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]);
71380 }
71381
71382 return pBasePtr;
71383 }
71384
71385
71386 typedef struct
71387 {
71388 size_t sizeInBytes;
71389 size_t inputBusOffset;
71390 size_t outputBusOffset;
71391 size_t cachedDataOffset;
71392 ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */
71393 ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */
71394 } ma_node_heap_layout;
71395
71396 static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)
71397 {
71398 ma_uint32 inputBusCount;
71399 ma_uint32 outputBusCount;
71400
71401 MA_ASSERT(pConfig != NULL);
71402 MA_ASSERT(pInputBusCount != NULL);
71403 MA_ASSERT(pOutputBusCount != NULL);
71404
71405 /* 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. */
71406 if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
71407 inputBusCount = pConfig->inputBusCount;
71408 } else {
71409 inputBusCount = pConfig->vtable->inputBusCount;
71410
71411 if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {
71412 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
71413 }
71414 }
71415
71416 if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
71417 outputBusCount = pConfig->outputBusCount;
71418 } else {
71419 outputBusCount = pConfig->vtable->outputBusCount;
71420
71421 if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {
71422 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
71423 }
71424 }
71425
71426 /* Bus counts must be within limits. */
71427 if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {
71428 return MA_INVALID_ARGS;
71429 }
71430
71431
71432 /* We must have channel counts for each bus. */
71433 if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {
71434 return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */
71435 }
71436
71437
71438 /* Some special rules for passthrough nodes. */
71439 if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
71440 if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) {
71441 return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */
71442 }
71443
71444 if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) {
71445 return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */
71446 }
71447 }
71448
71449
71450 *pInputBusCount = inputBusCount;
71451 *pOutputBusCount = outputBusCount;
71452
71453 return MA_SUCCESS;
71454 }
71455
71456 static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)
71457 {
71458 ma_result result;
71459 ma_uint32 inputBusCount;
71460 ma_uint32 outputBusCount;
71461
71462 MA_ASSERT(pHeapLayout != NULL);
71463
71464 MA_ZERO_OBJECT(pHeapLayout);
71465
71466 if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
71467 return MA_INVALID_ARGS;
71468 }
71469
71470 result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);
71471 if (result != MA_SUCCESS) {
71472 return result;
71473 }
71474
71475 pHeapLayout->sizeInBytes = 0;
71476
71477 /* Input buses. */
71478 if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
71479 pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;
71480 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount);
71481 } else {
71482 pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */
71483 }
71484
71485 /* Output buses. */
71486 if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
71487 pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;
71488 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount);
71489 } else {
71490 pHeapLayout->outputBusOffset = MA_SIZE_MAX;
71491 }
71492
71493 /*
71494 Cached audio data.
71495
71496 We need to allocate memory for a caching both input and output data. We have an optimization
71497 where no caching is necessary for specific conditions:
71498
71499 - The node has 0 inputs and 1 output.
71500
71501 When a node meets the above conditions, no cache is allocated.
71502
71503 The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by
71504 allocating too much, but at the same time we want it be large enough so that enough frames can
71505 be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For
71506 now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile
71507 time. It might also be worth investigating whether or not this can be configured at run time.
71508 */
71509 if (inputBusCount == 0 && outputBusCount == 1) {
71510 /* Fast path. No cache needed. */
71511 pHeapLayout->cachedDataOffset = MA_SIZE_MAX;
71512 } else {
71513 /* Slow path. Cache needed. */
71514 size_t cachedDataSizeInBytes = 0;
71515 ma_uint32 iBus;
71516
71517 for (iBus = 0; iBus < inputBusCount; iBus += 1) {
71518 cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
71519 }
71520
71521 for (iBus = 0; iBus < outputBusCount; iBus += 1) {
71522 cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
71523 }
71524
71525 pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
71526 pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes);
71527 }
71528
71529
71530 /*
71531 Not technically part of the heap, but we can output the input and output bus counts so we can
71532 avoid a redundant call to ma_node_translate_bus_counts().
71533 */
71534 pHeapLayout->inputBusCount = inputBusCount;
71535 pHeapLayout->outputBusCount = outputBusCount;
71536
71537 /* Make sure allocation size is aligned. */
71538 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
71539
71540 return MA_SUCCESS;
71541 }
71542
71543 MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes)
71544 {
71545 ma_result result;
71546 ma_node_heap_layout heapLayout;
71547
71548 if (pHeapSizeInBytes == NULL) {
71549 return MA_INVALID_ARGS;
71550 }
71551
71552 *pHeapSizeInBytes = 0;
71553
71554 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
71555 if (result != MA_SUCCESS) {
71556 return result;
71557 }
71558
71559 *pHeapSizeInBytes = heapLayout.sizeInBytes;
71560
71561 return MA_SUCCESS;
71562 }
71563
71564 MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)
71565 {
71566 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71567 ma_result result;
71568 ma_node_heap_layout heapLayout;
71569 ma_uint32 iInputBus;
71570 ma_uint32 iOutputBus;
71571
71572 if (pNodeBase == NULL) {
71573 return MA_INVALID_ARGS;
71574 }
71575
71576 MA_ZERO_OBJECT(pNodeBase);
71577
71578 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
71579 if (result != MA_SUCCESS) {
71580 return result;
71581 }
71582
71583 pNodeBase->_pHeap = pHeap;
71584 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
71585
71586 pNodeBase->pNodeGraph = pNodeGraph;
71587 pNodeBase->vtable = pConfig->vtable;
71588 pNodeBase->state = pConfig->initialState;
71589 pNodeBase->stateTimes[ma_node_state_started] = 0;
71590 pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */
71591 pNodeBase->inputBusCount = heapLayout.inputBusCount;
71592 pNodeBase->outputBusCount = heapLayout.outputBusCount;
71593
71594 if (heapLayout.inputBusOffset != MA_SIZE_MAX) {
71595 pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
71596 } else {
71597 pNodeBase->pInputBuses = pNodeBase->_inputBuses;
71598 }
71599
71600 if (heapLayout.outputBusOffset != MA_SIZE_MAX) {
71601 pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
71602 } else {
71603 pNodeBase->pOutputBuses = pNodeBase->_outputBuses;
71604 }
71605
71606 if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
71607 pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
71608 pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames;
71609 } else {
71610 pNodeBase->pCachedData = NULL;
71611 }
71612
71613
71614
71615 /* We need to run an initialization step for each input and output bus. */
71616 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
71617 result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
71618 if (result != MA_SUCCESS) {
71619 return result;
71620 }
71621 }
71622
71623 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
71624 result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);
71625 if (result != MA_SUCCESS) {
71626 return result;
71627 }
71628 }
71629
71630
71631 /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */
71632 if (pNodeBase->pCachedData != NULL) {
71633 ma_uint32 iBus;
71634
71635 #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. */
71636 /* For safety we'll go ahead and default the buffer to silence. */
71637 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
71638 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]));
71639 }
71640 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
71641 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]));
71642 }
71643 #else
71644 /* For debugging. Default to a sine wave. */
71645 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
71646 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);
71647 }
71648 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
71649 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);
71650 }
71651 #endif
71652 }
71653
71654 return MA_SUCCESS;
71655 }
71656
71657 MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
71658 {
71659 ma_result result;
71660 size_t heapSizeInBytes;
71661 void* pHeap;
71662
71663 result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes);
71664 if (result != MA_SUCCESS) {
71665 return result;
71666 }
71667
71668 if (heapSizeInBytes > 0) {
71669 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
71670 if (pHeap == NULL) {
71671 return MA_OUT_OF_MEMORY;
71672 }
71673 } else {
71674 pHeap = NULL;
71675 }
71676
71677 result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
71678 if (result != MA_SUCCESS) {
71679 ma_free(pHeap, pAllocationCallbacks);
71680 return result;
71681 }
71682
71683 ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
71684 return MA_SUCCESS;
71685 }
71686
71687 MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
71688 {
71689 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71690
71691 if (pNodeBase == NULL) {
71692 return;
71693 }
71694
71695 /*
71696 The first thing we need to do is fully detach the node. This will detach all inputs and
71697 outputs. We need to do this first because it will sever the connection with the node graph and
71698 allow us to complete uninitialization without needing to worry about thread-safety with the
71699 audio thread. The detachment process will wait for any local processing of the node to finish.
71700 */
71701 ma_node_detach_full(pNode);
71702
71703 /*
71704 At this point the node should be completely unreferenced by the node graph and we can finish up
71705 the uninitialization process without needing to worry about thread-safety.
71706 */
71707 if (pNodeBase->_ownsHeap) {
71708 ma_free(pNodeBase->_pHeap, pAllocationCallbacks);
71709 }
71710 }
71711
71712 MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode)
71713 {
71714 if (pNode == NULL) {
71715 return NULL;
71716 }
71717
71718 return ((const ma_node_base*)pNode)->pNodeGraph;
71719 }
71720
71721 MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode)
71722 {
71723 if (pNode == NULL) {
71724 return 0;
71725 }
71726
71727 return ((ma_node_base*)pNode)->inputBusCount;
71728 }
71729
71730 MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode)
71731 {
71732 if (pNode == NULL) {
71733 return 0;
71734 }
71735
71736 return ((ma_node_base*)pNode)->outputBusCount;
71737 }
71738
71739
71740 MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)
71741 {
71742 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
71743
71744 if (pNode == NULL) {
71745 return 0;
71746 }
71747
71748 if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {
71749 return 0; /* Invalid bus index. */
71750 }
71751
71752 return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);
71753 }
71754
71755 MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)
71756 {
71757 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
71758
71759 if (pNode == NULL) {
71760 return 0;
71761 }
71762
71763 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
71764 return 0; /* Invalid bus index. */
71765 }
71766
71767 return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);
71768 }
71769
71770
71771 static ma_result ma_node_detach_full(ma_node* pNode)
71772 {
71773 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71774 ma_uint32 iInputBus;
71775
71776 if (pNodeBase == NULL) {
71777 return MA_INVALID_ARGS;
71778 }
71779
71780 /*
71781 Make sure the node is completely detached first. This will not return until the output bus is
71782 guaranteed to no longer be referenced by the audio thread.
71783 */
71784 ma_node_detach_all_output_buses(pNode);
71785
71786 /*
71787 At this point all output buses will have been detached from the graph and we can be guaranteed
71788 that none of it's input nodes will be getting processed by the graph. We can detach these
71789 without needing to worry about the audio thread touching them.
71790 */
71791 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
71792 ma_node_input_bus* pInputBus;
71793 ma_node_output_bus* pOutputBus;
71794
71795 pInputBus = &pNodeBase->pInputBuses[iInputBus];
71796
71797 /*
71798 This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those
71799 functions are specifically for the audio thread. We'll instead just manually iterate using standard
71800 linked list logic. We don't need to worry about the audio thread referencing these because the step
71801 above severed the connection to the graph.
71802 */
71803 for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) {
71804 ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */
71805 }
71806 }
71807
71808 return MA_SUCCESS;
71809 }
71810
71811 MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex)
71812 {
71813 ma_result result = MA_SUCCESS;
71814 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71815 ma_node_base* pInputNodeBase;
71816
71817 if (pNode == NULL) {
71818 return MA_INVALID_ARGS;
71819 }
71820
71821 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
71822 return MA_INVALID_ARGS; /* Invalid output bus index. */
71823 }
71824
71825 /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */
71826 ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
71827 {
71828 pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
71829 if (pInputNodeBase != NULL) {
71830 ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);
71831 }
71832 }
71833 ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);
71834
71835 return result;
71836 }
71837
71838 MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode)
71839 {
71840 ma_uint32 iOutputBus;
71841
71842 if (pNode == NULL) {
71843 return MA_INVALID_ARGS;
71844 }
71845
71846 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {
71847 ma_node_detach_output_bus(pNode, iOutputBus);
71848 }
71849
71850 return MA_SUCCESS;
71851 }
71852
71853 MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)
71854 {
71855 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71856 ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;
71857
71858 if (pNodeBase == NULL || pOtherNodeBase == NULL) {
71859 return MA_INVALID_ARGS;
71860 }
71861
71862 if (pNodeBase == pOtherNodeBase) {
71863 return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */
71864 }
71865
71866 if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) {
71867 return MA_INVALID_OPERATION; /* Invalid bus index. */
71868 }
71869
71870 /* The output channel count of the output node must be the same as the input channel count of the input node. */
71871 if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) {
71872 return MA_INVALID_OPERATION; /* Channel count is incompatible. */
71873 }
71874
71875 /* This will deal with detaching if the output bus is already attached to something. */
71876 ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex);
71877
71878 return MA_SUCCESS;
71879 }
71880
71881 MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume)
71882 {
71883 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71884
71885 if (pNodeBase == NULL) {
71886 return MA_INVALID_ARGS;
71887 }
71888
71889 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
71890 return MA_INVALID_ARGS; /* Invalid bus index. */
71891 }
71892
71893 return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume);
71894 }
71895
71896 MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex)
71897 {
71898 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
71899
71900 if (pNodeBase == NULL) {
71901 return 0;
71902 }
71903
71904 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
71905 return 0; /* Invalid bus index. */
71906 }
71907
71908 return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]);
71909 }
71910
71911 MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state)
71912 {
71913 ma_node_base* pNodeBase = (ma_node_base*)pNode;
71914
71915 if (pNodeBase == NULL) {
71916 return MA_INVALID_ARGS;
71917 }
71918
71919 c89atomic_exchange_i32(&pNodeBase->state, state);
71920
71921 return MA_SUCCESS;
71922 }
71923
71924 MA_API ma_node_state ma_node_get_state(const ma_node* pNode)
71925 {
71926 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
71927
71928 if (pNodeBase == NULL) {
71929 return ma_node_state_stopped;
71930 }
71931
71932 return (ma_node_state)c89atomic_load_i32(&pNodeBase->state);
71933 }
71934
71935 MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime)
71936 {
71937 if (pNode == NULL) {
71938 return MA_INVALID_ARGS;
71939 }
71940
71941 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
71942 if (state != ma_node_state_started && state != ma_node_state_stopped) {
71943 return MA_INVALID_ARGS;
71944 }
71945
71946 c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime);
71947
71948 return MA_SUCCESS;
71949 }
71950
71951 MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state)
71952 {
71953 if (pNode == NULL) {
71954 return 0;
71955 }
71956
71957 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
71958 if (state != ma_node_state_started && state != ma_node_state_stopped) {
71959 return 0;
71960 }
71961
71962 return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]);
71963 }
71964
71965 MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime)
71966 {
71967 if (pNode == NULL) {
71968 return ma_node_state_stopped;
71969 }
71970
71971 return ma_node_get_state_by_time_range(pNode, globalTime, globalTime);
71972 }
71973
71974 MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
71975 {
71976 ma_node_state state;
71977
71978 if (pNode == NULL) {
71979 return ma_node_state_stopped;
71980 }
71981
71982 state = ma_node_get_state(pNode);
71983
71984 /* An explicitly stopped node is always stopped. */
71985 if (state == ma_node_state_stopped) {
71986 return ma_node_state_stopped;
71987 }
71988
71989 /*
71990 Getting here means the node is marked as started, but it may still not be truly started due to
71991 it's start time not having been reached yet. Also, the stop time may have also been reached in
71992 which case it'll be considered stopped.
71993 */
71994 if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) {
71995 return ma_node_state_stopped; /* Start time has not yet been reached. */
71996 }
71997
71998 if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) {
71999 return ma_node_state_stopped; /* Stop time has been reached. */
72000 }
72001
72002 /* Getting here means the node is marked as started and is within it's start/stop times. */
72003 return ma_node_state_started;
72004 }
72005
72006 MA_API ma_uint64 ma_node_get_time(const ma_node* pNode)
72007 {
72008 if (pNode == NULL) {
72009 return 0;
72010 }
72011
72012 return c89atomic_load_64(&((ma_node_base*)pNode)->localTime);
72013 }
72014
72015 MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime)
72016 {
72017 if (pNode == NULL) {
72018 return MA_INVALID_ARGS;
72019 }
72020
72021 c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime);
72022
72023 return MA_SUCCESS;
72024 }
72025
72026
72027
72028 static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72029 {
72030 ma_node_base* pNodeBase = (ma_node_base*)pNode;
72031
72032 MA_ASSERT(pNode != NULL);
72033
72034 if (pNodeBase->vtable->onProcess) {
72035 pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
72036 }
72037 }
72038
72039 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)
72040 {
72041 ma_node_base* pNodeBase = (ma_node_base*)pNode;
72042 ma_result result = MA_SUCCESS;
72043 ma_uint32 iInputBus;
72044 ma_uint32 iOutputBus;
72045 ma_uint32 inputBusCount;
72046 ma_uint32 outputBusCount;
72047 ma_uint32 totalFramesRead = 0;
72048 float* ppFramesIn[MA_MAX_NODE_BUS_COUNT];
72049 float* ppFramesOut[MA_MAX_NODE_BUS_COUNT];
72050 ma_uint64 globalTimeBeg;
72051 ma_uint64 globalTimeEnd;
72052 ma_uint64 startTime;
72053 ma_uint64 stopTime;
72054 ma_uint32 timeOffsetBeg;
72055 ma_uint32 timeOffsetEnd;
72056 ma_uint32 frameCountIn;
72057 ma_uint32 frameCountOut;
72058
72059 /*
72060 pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and
72061 expected that the number of frames read may be different to that requested. Therefore, the caller
72062 must look at this value to correctly determine how many frames were read.
72063 */
72064 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. */
72065 if (pFramesRead == NULL) {
72066 return MA_INVALID_ARGS;
72067 }
72068
72069 *pFramesRead = 0; /* Safety. */
72070
72071 if (pNodeBase == NULL) {
72072 return MA_INVALID_ARGS;
72073 }
72074
72075 if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) {
72076 return MA_INVALID_ARGS; /* Invalid output bus index. */
72077 }
72078
72079 /* Don't do anything if we're in a stopped state. */
72080 if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) {
72081 return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */
72082 }
72083
72084
72085 globalTimeBeg = globalTime;
72086 globalTimeEnd = globalTime + frameCount;
72087 startTime = ma_node_get_state_time(pNode, ma_node_state_started);
72088 stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped);
72089
72090 /*
72091 At this point we know that we are inside our start/stop times. However, we may need to adjust
72092 our frame count and output pointer to accomodate since we could be straddling the time period
72093 that this function is getting called for.
72094
72095 It's possible (and likely) that the start time does not line up with the output buffer. We
72096 therefore need to offset it by a number of frames to accomodate. The same thing applies for
72097 the stop time.
72098 */
72099 timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0;
72100 timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0;
72101
72102 /* Trim based on the start offset. We need to silence the start of the buffer. */
72103 if (timeOffsetBeg > 0) {
72104 ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
72105 pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);
72106 frameCount -= timeOffsetBeg;
72107 }
72108
72109 /* 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. */
72110 if (timeOffsetEnd > 0) {
72111 frameCount -= timeOffsetEnd;
72112 }
72113
72114
72115 /* We run on different paths depending on the bus counts. */
72116 inputBusCount = ma_node_get_input_bus_count(pNode);
72117 outputBusCount = ma_node_get_output_bus_count(pNode);
72118
72119 /*
72120 Run a simplified path when there are no inputs and one output. In this case there's nothing to
72121 actually read and we can go straight to output. This is a very common scenario because the vast
72122 majority of data source nodes will use this setup so this optimization I think is worthwhile.
72123 */
72124 if (inputBusCount == 0 && outputBusCount == 1) {
72125 /* Fast path. No need to read from input and no need for any caching. */
72126 frameCountIn = 0;
72127 frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */
72128
72129 ppFramesOut[0] = pFramesOut;
72130
72131 /*
72132 If it's a passthrough we won't be expecting the callback to output anything, so we'll
72133 need to pre-silence the output buffer.
72134 */
72135 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
72136 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
72137 }
72138
72139 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
72140 totalFramesRead = frameCountOut;
72141 } else {
72142 /* Slow path. Need to read input data. */
72143 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
72144 /*
72145 Fast path. We're running a passthrough. We need to read directly into the output buffer, but
72146 still fire the callback so that event handling and trigger nodes can do their thing. Since
72147 it's a passthrough there's no need for any kind of caching logic.
72148 */
72149 MA_ASSERT(outputBusCount == inputBusCount);
72150 MA_ASSERT(outputBusCount == 1);
72151 MA_ASSERT(outputBusIndex == 0);
72152
72153 /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */
72154 ppFramesOut[0] = pFramesOut;
72155 ppFramesIn[0] = ppFramesOut[0];
72156
72157 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime);
72158 if (result == MA_SUCCESS) {
72159 /* Even though it's a passthrough, we still need to fire the callback. */
72160 frameCountIn = totalFramesRead;
72161 frameCountOut = totalFramesRead;
72162
72163 if (totalFramesRead > 0) {
72164 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. */
72165 }
72166
72167 /*
72168 A passthrough should never have modified the input and output frame counts. If you're
72169 triggering these assers you need to fix your processing callback.
72170 */
72171 MA_ASSERT(frameCountIn == totalFramesRead);
72172 MA_ASSERT(frameCountOut == totalFramesRead);
72173 }
72174 } else {
72175 /* Slow path. Need to do caching. */
72176 ma_uint32 framesToProcessIn;
72177 ma_uint32 framesToProcessOut;
72178 ma_bool32 consumeNullInput = MA_FALSE;
72179
72180 /*
72181 We use frameCount as a basis for the number of frames to read since that's what's being
72182 requested, however we still need to clamp it to whatever can fit in the cache.
72183
72184 This will also be used as the basis for determining how many input frames to read. This is
72185 not ideal because it can result in too many input frames being read which introduces latency.
72186 To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount
72187 which is used as hint to miniaudio as to how many input frames it needs to read at a time. This
72188 callback is completely optional, and if it's not set, miniaudio will assume `frameCount`.
72189
72190 This function will be called multiple times for each period of time, once for each output node.
72191 We cannot read from each input node each time this function is called. Instead we need to check
72192 whether or not this is first output bus to be read from for this time period, and if so, read
72193 from our input data.
72194
72195 To determine whether or not we're ready to read data, we check a flag. There will be one flag
72196 for each output. When the flag is set, it means data has been read previously and that we're
72197 ready to advance time forward for our input nodes by reading fresh data.
72198 */
72199 framesToProcessOut = frameCount;
72200 if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) {
72201 framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus;
72202 }
72203
72204 framesToProcessIn = frameCount;
72205 if (pNodeBase->vtable->onGetRequiredInputFrameCount) {
72206 pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */
72207 }
72208 if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) {
72209 framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus;
72210 }
72211
72212
72213 MA_ASSERT(framesToProcessIn <= 0xFFFF);
72214 MA_ASSERT(framesToProcessOut <= 0xFFFF);
72215
72216 if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) {
72217 /* Getting here means we need to do another round of processing. */
72218 pNodeBase->cachedFrameCountOut = 0;
72219
72220 for (;;) {
72221 frameCountOut = 0;
72222
72223 /*
72224 We need to prepare our output frame pointers for processing. In the same iteration we need
72225 to mark every output bus as unread so that future calls to this function for different buses
72226 for the current time period don't pull in data when they should instead be reading from cache.
72227 */
72228 for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) {
72229 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. */
72230 ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus);
72231 }
72232
72233 /* We only need to read from input buses if there isn't already some data in the cache. */
72234 if (pNodeBase->cachedFrameCountIn == 0) {
72235 ma_uint32 maxFramesReadIn = 0;
72236
72237 /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */
72238 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
72239 ma_uint32 framesRead;
72240
72241 /* The first thing to do is get the offset within our bulk allocation to store this input data. */
72242 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);
72243
72244 /* 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. */
72245 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);
72246 if (result != MA_SUCCESS) {
72247 /* It doesn't really matter if we fail because we'll just fill with silence. */
72248 framesRead = 0; /* Just for safety, but I don't think it's really needed. */
72249 }
72250
72251 /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */
72252 /* Any leftover frames need to silenced for safety. */
72253 if (framesRead < framesToProcessIn) {
72254 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));
72255 }
72256
72257 maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);
72258 }
72259
72260 /* This was a fresh load of input data so reset our consumption counter. */
72261 pNodeBase->consumedFrameCountIn = 0;
72262
72263 /*
72264 We don't want to keep processing if there's nothing to process, so set the number of cached
72265 input frames to the maximum number we read from each attachment (the lesser will be padded
72266 with silence). If we didn't read anything, this will be set to 0 and the entire buffer will
72267 have been assigned to silence. This being equal to 0 is an important property for us because
72268 it allows us to detect when NULL can be passed into the processing callback for the input
72269 buffer for the purpose of continuous processing.
72270 */
72271 pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;
72272 } else {
72273 /* We don't need to read anything, but we do need to prepare our input frame pointers. */
72274 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
72275 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));
72276 }
72277 }
72278
72279 /*
72280 At this point we have our input data so now we need to do some processing. Sneaky little
72281 optimization here - we can set the pointer to the output buffer for this output bus so
72282 that the final copy into the output buffer is done directly by onProcess().
72283 */
72284 if (pFramesOut != NULL) {
72285 ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex));
72286 }
72287
72288
72289 /* Give the processing function the entire capacity of the output buffer. */
72290 frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut);
72291
72292 /*
72293 We need to treat nodes with continuous processing a little differently. For these ones,
72294 we always want to fire the callback with the requested number of frames, regardless of
72295 pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass
72296 in NULL for the input buffer to the callback.
72297 */
72298 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {
72299 /* We're using continuous processing. Make sure we specify the whole frame count at all times. */
72300 frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */
72301
72302 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {
72303 consumeNullInput = MA_TRUE;
72304 } else {
72305 consumeNullInput = MA_FALSE;
72306 }
72307
72308 /*
72309 Since we're using continuous processing we're always passing in a full frame count
72310 regardless of how much input data was read. If this is greater than what we read as
72311 input, we'll end up with an underflow. We instead need to make sure our cached frame
72312 count is set to the number of frames we'll be passing to the data callback. Not
72313 doing this will result in an underflow when we "consume" the cached data later on.
72314
72315 Note that this check needs to be done after the "consumeNullInput" check above because
72316 we use the property of cachedFrameCountIn being 0 to determine whether or not we
72317 should be passing in a null pointer to the processing callback for when the node is
72318 configured with MA_NODE_FLAG_ALLOW_NULL_INPUT.
72319 */
72320 if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) {
72321 pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn;
72322 }
72323 } else {
72324 frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */
72325 consumeNullInput = MA_FALSE;
72326 }
72327
72328 /*
72329 Process data slightly differently depending on whether or not we're consuming NULL
72330 input (checked just above).
72331 */
72332 if (consumeNullInput) {
72333 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
72334 } else {
72335 /*
72336 We want to skip processing if there's no input data, but we can only do that safely if
72337 we know that there is no chance of any output frames being produced. If continuous
72338 processing is being used, this won't be a problem because the input frame count will
72339 always be non-0. However, if continuous processing is *not* enabled and input and output
72340 data is processed at different rates, we still need to process that last input frame
72341 because there could be a few excess output frames needing to be produced from cached
72342 data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for
72343 determining whether or not we need to process the node even when there are no input
72344 frames available right now.
72345 */
72346 if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
72347 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. */
72348 } else {
72349 frameCountOut = 0; /* No data was processed. */
72350 }
72351 }
72352
72353 /*
72354 Thanks to our sneaky optimization above we don't need to do any data copying directly into
72355 the output buffer - the onProcess() callback just did that for us. We do, however, need to
72356 apply the number of input and output frames that were processed. Note that due to continuous
72357 processing above, we need to do explicit checks here. If we just consumed a NULL input
72358 buffer it means that no actual input data was processed from the internal buffers and we
72359 don't want to be modifying any counters.
72360 */
72361 if (consumeNullInput == MA_FALSE) {
72362 pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;
72363 pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn;
72364 }
72365
72366 /* The cached output frame count is always equal to what we just read. */
72367 pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut;
72368
72369 /* 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. */
72370 if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) {
72371 break;
72372 }
72373 }
72374 } else {
72375 /*
72376 We're not needing to read anything from the input buffer so just read directly from our
72377 already-processed data.
72378 */
72379 if (pFramesOut != NULL) {
72380 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));
72381 }
72382 }
72383
72384 /* The number of frames read is always equal to the number of cached output frames. */
72385 totalFramesRead = pNodeBase->cachedFrameCountOut;
72386
72387 /* Now that we've read the data, make sure our read flag is set. */
72388 ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE);
72389 }
72390 }
72391
72392 /* Apply volume, if necessary. */
72393 ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]));
72394
72395 /* Advance our local time forward. */
72396 c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead);
72397
72398 *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */
72399 return result;
72400 }
72401
72402
72403
72404
72405 /* Data source node. */
72406 MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource)
72407 {
72408 ma_data_source_node_config config;
72409
72410 MA_ZERO_OBJECT(&config);
72411 config.nodeConfig = ma_node_config_init();
72412 config.pDataSource = pDataSource;
72413
72414 return config;
72415 }
72416
72417
72418 static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72419 {
72420 ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode;
72421 ma_format format;
72422 ma_uint32 channels;
72423 ma_uint32 frameCount;
72424 ma_uint64 framesRead = 0;
72425
72426 MA_ASSERT(pDataSourceNode != NULL);
72427 MA_ASSERT(pDataSourceNode->pDataSource != NULL);
72428 MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0);
72429 MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1);
72430
72431 /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */
72432 (void)ppFramesIn;
72433 (void)pFrameCountIn;
72434
72435 frameCount = *pFrameCountOut;
72436
72437 /* miniaudio should never be calling this with a frame count of zero. */
72438 MA_ASSERT(frameCount > 0);
72439
72440 if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */
72441 /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */
72442 MA_ASSERT(format == ma_format_f32);
72443 (void)format; /* Just to silence some static analysis tools. */
72444
72445 ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead);
72446 }
72447
72448 *pFrameCountOut = (ma_uint32)framesRead;
72449 }
72450
72451 static ma_node_vtable g_ma_data_source_node_vtable =
72452 {
72453 ma_data_source_node_process_pcm_frames,
72454 NULL, /* onGetRequiredInputFrameCount */
72455 0, /* 0 input buses. */
72456 1, /* 1 output bus. */
72457 0
72458 };
72459
72460 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)
72461 {
72462 ma_result result;
72463 ma_format format; /* For validating the format, which must be ma_format_f32. */
72464 ma_uint32 channels; /* For specifying the channel count of the output bus. */
72465 ma_node_config baseConfig;
72466
72467 if (pDataSourceNode == NULL) {
72468 return MA_INVALID_ARGS;
72469 }
72470
72471 MA_ZERO_OBJECT(pDataSourceNode);
72472
72473 if (pConfig == NULL) {
72474 return MA_INVALID_ARGS;
72475 }
72476
72477 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. */
72478 if (result != MA_SUCCESS) {
72479 return result;
72480 }
72481
72482 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. */
72483 if (format != ma_format_f32) {
72484 return MA_INVALID_ARGS; /* Invalid format. */
72485 }
72486
72487 /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */
72488 baseConfig = pConfig->nodeConfig;
72489 baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */
72490
72491 /*
72492 The channel count is defined by the data source. It is invalid for the caller to manually set
72493 the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the
72494 channel count pointer to NULL which is how it must remain. If you trigger any of these asserts
72495 it means you're explicitly setting the channel count. Instead, configure the output channel
72496 count of your data source to be the necessary channel count.
72497 */
72498 if (baseConfig.pOutputChannels != NULL) {
72499 return MA_INVALID_ARGS;
72500 }
72501
72502 baseConfig.pOutputChannels = &channels;
72503
72504 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base);
72505 if (result != MA_SUCCESS) {
72506 return result;
72507 }
72508
72509 pDataSourceNode->pDataSource = pConfig->pDataSource;
72510
72511 return MA_SUCCESS;
72512 }
72513
72514 MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks)
72515 {
72516 ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks);
72517 }
72518
72519 MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping)
72520 {
72521 if (pDataSourceNode == NULL) {
72522 return MA_INVALID_ARGS;
72523 }
72524
72525 return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping);
72526 }
72527
72528 MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode)
72529 {
72530 if (pDataSourceNode == NULL) {
72531 return MA_FALSE;
72532 }
72533
72534 return ma_data_source_is_looping(pDataSourceNode->pDataSource);
72535 }
72536
72537
72538
72539 /* Splitter Node. */
72540 MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)
72541 {
72542 ma_splitter_node_config config;
72543
72544 MA_ZERO_OBJECT(&config);
72545 config.nodeConfig = ma_node_config_init();
72546 config.channels = channels;
72547 config.outputBusCount = 2;
72548
72549 return config;
72550 }
72551
72552
72553 static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72554 {
72555 ma_node_base* pNodeBase = (ma_node_base*)pNode;
72556 ma_uint32 iOutputBus;
72557 ma_uint32 channels;
72558
72559 MA_ASSERT(pNodeBase != NULL);
72560 MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1);
72561
72562 /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */
72563 (void)pFrameCountIn;
72564
72565 /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */
72566 channels = ma_node_get_input_channels(pNodeBase, 0);
72567
72568 /* Splitting is just copying the first input bus and copying it over to each output bus. */
72569 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
72570 ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels);
72571 }
72572 }
72573
72574 static ma_node_vtable g_ma_splitter_node_vtable =
72575 {
72576 ma_splitter_node_process_pcm_frames,
72577 NULL, /* onGetRequiredInputFrameCount */
72578 1, /* 1 input bus. */
72579 MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */
72580 0
72581 };
72582
72583 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)
72584 {
72585 ma_result result;
72586 ma_node_config baseConfig;
72587 ma_uint32 pInputChannels[1];
72588 ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT];
72589 ma_uint32 iOutputBus;
72590
72591 if (pSplitterNode == NULL) {
72592 return MA_INVALID_ARGS;
72593 }
72594
72595 MA_ZERO_OBJECT(pSplitterNode);
72596
72597 if (pConfig == NULL) {
72598 return MA_INVALID_ARGS;
72599 }
72600
72601 if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) {
72602 return MA_INVALID_ARGS; /* Too many output buses. */
72603 }
72604
72605 /* Splitters require the same number of channels between inputs and outputs. */
72606 pInputChannels[0] = pConfig->channels;
72607 for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) {
72608 pOutputChannels[iOutputBus] = pConfig->channels;
72609 }
72610
72611 baseConfig = pConfig->nodeConfig;
72612 baseConfig.vtable = &g_ma_splitter_node_vtable;
72613 baseConfig.pInputChannels = pInputChannels;
72614 baseConfig.pOutputChannels = pOutputChannels;
72615 baseConfig.outputBusCount = pConfig->outputBusCount;
72616
72617 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base);
72618 if (result != MA_SUCCESS) {
72619 return result; /* Failed to initialize the base node. */
72620 }
72621
72622 return MA_SUCCESS;
72623 }
72624
72625 MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks)
72626 {
72627 ma_node_uninit(pSplitterNode, pAllocationCallbacks);
72628 }
72629
72630
72631 /*
72632 Biquad Node
72633 */
72634 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)
72635 {
72636 ma_biquad_node_config config;
72637
72638 config.nodeConfig = ma_node_config_init();
72639 config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
72640
72641 return config;
72642 }
72643
72644 static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72645 {
72646 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
72647
72648 MA_ASSERT(pNode != NULL);
72649 (void)pFrameCountIn;
72650
72651 ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
72652 }
72653
72654 static ma_node_vtable g_ma_biquad_node_vtable =
72655 {
72656 ma_biquad_node_process_pcm_frames,
72657 NULL, /* onGetRequiredInputFrameCount */
72658 1, /* One input. */
72659 1, /* One output. */
72660 0 /* Default flags. */
72661 };
72662
72663 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)
72664 {
72665 ma_result result;
72666 ma_node_config baseNodeConfig;
72667
72668 if (pNode == NULL) {
72669 return MA_INVALID_ARGS;
72670 }
72671
72672 MA_ZERO_OBJECT(pNode);
72673
72674 if (pConfig == NULL) {
72675 return MA_INVALID_ARGS;
72676 }
72677
72678 if (pConfig->biquad.format != ma_format_f32) {
72679 return MA_INVALID_ARGS; /* The format must be f32. */
72680 }
72681
72682 result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad);
72683 if (result != MA_SUCCESS) {
72684 return result;
72685 }
72686
72687 baseNodeConfig = ma_node_config_init();
72688 baseNodeConfig.vtable = &g_ma_biquad_node_vtable;
72689 baseNodeConfig.pInputChannels = &pConfig->biquad.channels;
72690 baseNodeConfig.pOutputChannels = &pConfig->biquad.channels;
72691
72692 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
72693 if (result != MA_SUCCESS) {
72694 return result;
72695 }
72696
72697 return result;
72698 }
72699
72700 MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode)
72701 {
72702 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
72703
72704 MA_ASSERT(pNode != NULL);
72705
72706 return ma_biquad_reinit(pConfig, &pLPFNode->biquad);
72707 }
72708
72709 MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
72710 {
72711 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
72712
72713 if (pNode == NULL) {
72714 return;
72715 }
72716
72717 ma_node_uninit(pNode, pAllocationCallbacks);
72718 ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks);
72719 }
72720
72721
72722
72723 /*
72724 Low Pass Filter Node
72725 */
72726 MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
72727 {
72728 ma_lpf_node_config config;
72729
72730 config.nodeConfig = ma_node_config_init();
72731 config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
72732
72733 return config;
72734 }
72735
72736 static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72737 {
72738 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
72739
72740 MA_ASSERT(pNode != NULL);
72741 (void)pFrameCountIn;
72742
72743 ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
72744 }
72745
72746 static ma_node_vtable g_ma_lpf_node_vtable =
72747 {
72748 ma_lpf_node_process_pcm_frames,
72749 NULL, /* onGetRequiredInputFrameCount */
72750 1, /* One input. */
72751 1, /* One output. */
72752 0 /* Default flags. */
72753 };
72754
72755 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)
72756 {
72757 ma_result result;
72758 ma_node_config baseNodeConfig;
72759
72760 if (pNode == NULL) {
72761 return MA_INVALID_ARGS;
72762 }
72763
72764 MA_ZERO_OBJECT(pNode);
72765
72766 if (pConfig == NULL) {
72767 return MA_INVALID_ARGS;
72768 }
72769
72770 if (pConfig->lpf.format != ma_format_f32) {
72771 return MA_INVALID_ARGS; /* The format must be f32. */
72772 }
72773
72774 result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf);
72775 if (result != MA_SUCCESS) {
72776 return result;
72777 }
72778
72779 baseNodeConfig = ma_node_config_init();
72780 baseNodeConfig.vtable = &g_ma_lpf_node_vtable;
72781 baseNodeConfig.pInputChannels = &pConfig->lpf.channels;
72782 baseNodeConfig.pOutputChannels = &pConfig->lpf.channels;
72783
72784 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
72785 if (result != MA_SUCCESS) {
72786 return result;
72787 }
72788
72789 return result;
72790 }
72791
72792 MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode)
72793 {
72794 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
72795
72796 if (pNode == NULL) {
72797 return MA_INVALID_ARGS;
72798 }
72799
72800 return ma_lpf_reinit(pConfig, &pLPFNode->lpf);
72801 }
72802
72803 MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
72804 {
72805 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
72806
72807 if (pNode == NULL) {
72808 return;
72809 }
72810
72811 ma_node_uninit(pNode, pAllocationCallbacks);
72812 ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks);
72813 }
72814
72815
72816
72817 /*
72818 High Pass Filter Node
72819 */
72820 MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
72821 {
72822 ma_hpf_node_config config;
72823
72824 config.nodeConfig = ma_node_config_init();
72825 config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
72826
72827 return config;
72828 }
72829
72830 static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72831 {
72832 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
72833
72834 MA_ASSERT(pNode != NULL);
72835 (void)pFrameCountIn;
72836
72837 ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
72838 }
72839
72840 static ma_node_vtable g_ma_hpf_node_vtable =
72841 {
72842 ma_hpf_node_process_pcm_frames,
72843 NULL, /* onGetRequiredInputFrameCount */
72844 1, /* One input. */
72845 1, /* One output. */
72846 0 /* Default flags. */
72847 };
72848
72849 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)
72850 {
72851 ma_result result;
72852 ma_node_config baseNodeConfig;
72853
72854 if (pNode == NULL) {
72855 return MA_INVALID_ARGS;
72856 }
72857
72858 MA_ZERO_OBJECT(pNode);
72859
72860 if (pConfig == NULL) {
72861 return MA_INVALID_ARGS;
72862 }
72863
72864 if (pConfig->hpf.format != ma_format_f32) {
72865 return MA_INVALID_ARGS; /* The format must be f32. */
72866 }
72867
72868 result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf);
72869 if (result != MA_SUCCESS) {
72870 return result;
72871 }
72872
72873 baseNodeConfig = ma_node_config_init();
72874 baseNodeConfig.vtable = &g_ma_hpf_node_vtable;
72875 baseNodeConfig.pInputChannels = &pConfig->hpf.channels;
72876 baseNodeConfig.pOutputChannels = &pConfig->hpf.channels;
72877
72878 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
72879 if (result != MA_SUCCESS) {
72880 return result;
72881 }
72882
72883 return result;
72884 }
72885
72886 MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode)
72887 {
72888 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
72889
72890 if (pNode == NULL) {
72891 return MA_INVALID_ARGS;
72892 }
72893
72894 return ma_hpf_reinit(pConfig, &pHPFNode->hpf);
72895 }
72896
72897 MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
72898 {
72899 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
72900
72901 if (pNode == NULL) {
72902 return;
72903 }
72904
72905 ma_node_uninit(pNode, pAllocationCallbacks);
72906 ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks);
72907 }
72908
72909
72910
72911
72912 /*
72913 Band Pass Filter Node
72914 */
72915 MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
72916 {
72917 ma_bpf_node_config config;
72918
72919 config.nodeConfig = ma_node_config_init();
72920 config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
72921
72922 return config;
72923 }
72924
72925 static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
72926 {
72927 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
72928
72929 MA_ASSERT(pNode != NULL);
72930 (void)pFrameCountIn;
72931
72932 ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
72933 }
72934
72935 static ma_node_vtable g_ma_bpf_node_vtable =
72936 {
72937 ma_bpf_node_process_pcm_frames,
72938 NULL, /* onGetRequiredInputFrameCount */
72939 1, /* One input. */
72940 1, /* One output. */
72941 0 /* Default flags. */
72942 };
72943
72944 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)
72945 {
72946 ma_result result;
72947 ma_node_config baseNodeConfig;
72948
72949 if (pNode == NULL) {
72950 return MA_INVALID_ARGS;
72951 }
72952
72953 MA_ZERO_OBJECT(pNode);
72954
72955 if (pConfig == NULL) {
72956 return MA_INVALID_ARGS;
72957 }
72958
72959 if (pConfig->bpf.format != ma_format_f32) {
72960 return MA_INVALID_ARGS; /* The format must be f32. */
72961 }
72962
72963 result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf);
72964 if (result != MA_SUCCESS) {
72965 return result;
72966 }
72967
72968 baseNodeConfig = ma_node_config_init();
72969 baseNodeConfig.vtable = &g_ma_bpf_node_vtable;
72970 baseNodeConfig.pInputChannels = &pConfig->bpf.channels;
72971 baseNodeConfig.pOutputChannels = &pConfig->bpf.channels;
72972
72973 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
72974 if (result != MA_SUCCESS) {
72975 return result;
72976 }
72977
72978 return result;
72979 }
72980
72981 MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode)
72982 {
72983 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
72984
72985 if (pNode == NULL) {
72986 return MA_INVALID_ARGS;
72987 }
72988
72989 return ma_bpf_reinit(pConfig, &pBPFNode->bpf);
72990 }
72991
72992 MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
72993 {
72994 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
72995
72996 if (pNode == NULL) {
72997 return;
72998 }
72999
73000 ma_node_uninit(pNode, pAllocationCallbacks);
73001 ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks);
73002 }
73003
73004
73005
73006 /*
73007 Notching Filter Node
73008 */
73009 MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
73010 {
73011 ma_notch_node_config config;
73012
73013 config.nodeConfig = ma_node_config_init();
73014 config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency);
73015
73016 return config;
73017 }
73018
73019 static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73020 {
73021 ma_notch_node* pBPFNode = (ma_notch_node*)pNode;
73022
73023 MA_ASSERT(pNode != NULL);
73024 (void)pFrameCountIn;
73025
73026 ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73027 }
73028
73029 static ma_node_vtable g_ma_notch_node_vtable =
73030 {
73031 ma_notch_node_process_pcm_frames,
73032 NULL, /* onGetRequiredInputFrameCount */
73033 1, /* One input. */
73034 1, /* One output. */
73035 0 /* Default flags. */
73036 };
73037
73038 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)
73039 {
73040 ma_result result;
73041 ma_node_config baseNodeConfig;
73042
73043 if (pNode == NULL) {
73044 return MA_INVALID_ARGS;
73045 }
73046
73047 MA_ZERO_OBJECT(pNode);
73048
73049 if (pConfig == NULL) {
73050 return MA_INVALID_ARGS;
73051 }
73052
73053 if (pConfig->notch.format != ma_format_f32) {
73054 return MA_INVALID_ARGS; /* The format must be f32. */
73055 }
73056
73057 result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch);
73058 if (result != MA_SUCCESS) {
73059 return result;
73060 }
73061
73062 baseNodeConfig = ma_node_config_init();
73063 baseNodeConfig.vtable = &g_ma_notch_node_vtable;
73064 baseNodeConfig.pInputChannels = &pConfig->notch.channels;
73065 baseNodeConfig.pOutputChannels = &pConfig->notch.channels;
73066
73067 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73068 if (result != MA_SUCCESS) {
73069 return result;
73070 }
73071
73072 return result;
73073 }
73074
73075 MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode)
73076 {
73077 ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
73078
73079 if (pNode == NULL) {
73080 return MA_INVALID_ARGS;
73081 }
73082
73083 return ma_notch2_reinit(pConfig, &pNotchNode->notch);
73084 }
73085
73086 MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73087 {
73088 ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
73089
73090 if (pNode == NULL) {
73091 return;
73092 }
73093
73094 ma_node_uninit(pNode, pAllocationCallbacks);
73095 ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks);
73096 }
73097
73098
73099
73100 /*
73101 Peaking Filter Node
73102 */
73103 MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
73104 {
73105 ma_peak_node_config config;
73106
73107 config.nodeConfig = ma_node_config_init();
73108 config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
73109
73110 return config;
73111 }
73112
73113 static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73114 {
73115 ma_peak_node* pBPFNode = (ma_peak_node*)pNode;
73116
73117 MA_ASSERT(pNode != NULL);
73118 (void)pFrameCountIn;
73119
73120 ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73121 }
73122
73123 static ma_node_vtable g_ma_peak_node_vtable =
73124 {
73125 ma_peak_node_process_pcm_frames,
73126 NULL, /* onGetRequiredInputFrameCount */
73127 1, /* One input. */
73128 1, /* One output. */
73129 0 /* Default flags. */
73130 };
73131
73132 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)
73133 {
73134 ma_result result;
73135 ma_node_config baseNodeConfig;
73136
73137 if (pNode == NULL) {
73138 return MA_INVALID_ARGS;
73139 }
73140
73141 MA_ZERO_OBJECT(pNode);
73142
73143 if (pConfig == NULL) {
73144 return MA_INVALID_ARGS;
73145 }
73146
73147 if (pConfig->peak.format != ma_format_f32) {
73148 return MA_INVALID_ARGS; /* The format must be f32. */
73149 }
73150
73151 result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak);
73152 if (result != MA_SUCCESS) {
73153 ma_node_uninit(pNode, pAllocationCallbacks);
73154 return result;
73155 }
73156
73157 baseNodeConfig = ma_node_config_init();
73158 baseNodeConfig.vtable = &g_ma_peak_node_vtable;
73159 baseNodeConfig.pInputChannels = &pConfig->peak.channels;
73160 baseNodeConfig.pOutputChannels = &pConfig->peak.channels;
73161
73162 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73163 if (result != MA_SUCCESS) {
73164 return result;
73165 }
73166
73167 return result;
73168 }
73169
73170 MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode)
73171 {
73172 ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
73173
73174 if (pNode == NULL) {
73175 return MA_INVALID_ARGS;
73176 }
73177
73178 return ma_peak2_reinit(pConfig, &pPeakNode->peak);
73179 }
73180
73181 MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73182 {
73183 ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
73184
73185 if (pNode == NULL) {
73186 return;
73187 }
73188
73189 ma_node_uninit(pNode, pAllocationCallbacks);
73190 ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks);
73191 }
73192
73193
73194
73195 /*
73196 Low Shelf Filter Node
73197 */
73198 MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
73199 {
73200 ma_loshelf_node_config config;
73201
73202 config.nodeConfig = ma_node_config_init();
73203 config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
73204
73205 return config;
73206 }
73207
73208 static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73209 {
73210 ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;
73211
73212 MA_ASSERT(pNode != NULL);
73213 (void)pFrameCountIn;
73214
73215 ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73216 }
73217
73218 static ma_node_vtable g_ma_loshelf_node_vtable =
73219 {
73220 ma_loshelf_node_process_pcm_frames,
73221 NULL, /* onGetRequiredInputFrameCount */
73222 1, /* One input. */
73223 1, /* One output. */
73224 0 /* Default flags. */
73225 };
73226
73227 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)
73228 {
73229 ma_result result;
73230 ma_node_config baseNodeConfig;
73231
73232 if (pNode == NULL) {
73233 return MA_INVALID_ARGS;
73234 }
73235
73236 MA_ZERO_OBJECT(pNode);
73237
73238 if (pConfig == NULL) {
73239 return MA_INVALID_ARGS;
73240 }
73241
73242 if (pConfig->loshelf.format != ma_format_f32) {
73243 return MA_INVALID_ARGS; /* The format must be f32. */
73244 }
73245
73246 result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf);
73247 if (result != MA_SUCCESS) {
73248 return result;
73249 }
73250
73251 baseNodeConfig = ma_node_config_init();
73252 baseNodeConfig.vtable = &g_ma_loshelf_node_vtable;
73253 baseNodeConfig.pInputChannels = &pConfig->loshelf.channels;
73254 baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels;
73255
73256 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73257 if (result != MA_SUCCESS) {
73258 return result;
73259 }
73260
73261 return result;
73262 }
73263
73264 MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode)
73265 {
73266 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
73267
73268 if (pNode == NULL) {
73269 return MA_INVALID_ARGS;
73270 }
73271
73272 return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf);
73273 }
73274
73275 MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73276 {
73277 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
73278
73279 if (pNode == NULL) {
73280 return;
73281 }
73282
73283 ma_node_uninit(pNode, pAllocationCallbacks);
73284 ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks);
73285 }
73286
73287
73288
73289 /*
73290 High Shelf Filter Node
73291 */
73292 MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
73293 {
73294 ma_hishelf_node_config config;
73295
73296 config.nodeConfig = ma_node_config_init();
73297 config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
73298
73299 return config;
73300 }
73301
73302 static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73303 {
73304 ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;
73305
73306 MA_ASSERT(pNode != NULL);
73307 (void)pFrameCountIn;
73308
73309 ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73310 }
73311
73312 static ma_node_vtable g_ma_hishelf_node_vtable =
73313 {
73314 ma_hishelf_node_process_pcm_frames,
73315 NULL, /* onGetRequiredInputFrameCount */
73316 1, /* One input. */
73317 1, /* One output. */
73318 0 /* Default flags. */
73319 };
73320
73321 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)
73322 {
73323 ma_result result;
73324 ma_node_config baseNodeConfig;
73325
73326 if (pNode == NULL) {
73327 return MA_INVALID_ARGS;
73328 }
73329
73330 MA_ZERO_OBJECT(pNode);
73331
73332 if (pConfig == NULL) {
73333 return MA_INVALID_ARGS;
73334 }
73335
73336 if (pConfig->hishelf.format != ma_format_f32) {
73337 return MA_INVALID_ARGS; /* The format must be f32. */
73338 }
73339
73340 result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf);
73341 if (result != MA_SUCCESS) {
73342 return result;
73343 }
73344
73345 baseNodeConfig = ma_node_config_init();
73346 baseNodeConfig.vtable = &g_ma_hishelf_node_vtable;
73347 baseNodeConfig.pInputChannels = &pConfig->hishelf.channels;
73348 baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels;
73349
73350 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
73351 if (result != MA_SUCCESS) {
73352 return result;
73353 }
73354
73355 return result;
73356 }
73357
73358 MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode)
73359 {
73360 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
73361
73362 if (pNode == NULL) {
73363 return MA_INVALID_ARGS;
73364 }
73365
73366 return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf);
73367 }
73368
73369 MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
73370 {
73371 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
73372
73373 if (pNode == NULL) {
73374 return;
73375 }
73376
73377 ma_node_uninit(pNode, pAllocationCallbacks);
73378 ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks);
73379 }
73380
73381
73382
73383
73384 MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
73385 {
73386 ma_delay_node_config config;
73387
73388 config.nodeConfig = ma_node_config_init();
73389 config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay);
73390
73391 return config;
73392 }
73393
73394
73395 static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73396 {
73397 ma_delay_node* pDelayNode = (ma_delay_node*)pNode;
73398
73399 (void)pFrameCountIn;
73400
73401 ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
73402 }
73403
73404 static ma_node_vtable g_ma_delay_node_vtable =
73405 {
73406 ma_delay_node_process_pcm_frames,
73407 NULL,
73408 1, /* 1 input channels. */
73409 1, /* 1 output channel. */
73410 MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */
73411 };
73412
73413 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)
73414 {
73415 ma_result result;
73416 ma_node_config baseConfig;
73417
73418 if (pDelayNode == NULL) {
73419 return MA_INVALID_ARGS;
73420 }
73421
73422 MA_ZERO_OBJECT(pDelayNode);
73423
73424 result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay);
73425 if (result != MA_SUCCESS) {
73426 return result;
73427 }
73428
73429 baseConfig = pConfig->nodeConfig;
73430 baseConfig.vtable = &g_ma_delay_node_vtable;
73431 baseConfig.pInputChannels = &pConfig->delay.channels;
73432 baseConfig.pOutputChannels = &pConfig->delay.channels;
73433
73434 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode);
73435 if (result != MA_SUCCESS) {
73436 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
73437 return result;
73438 }
73439
73440 return result;
73441 }
73442
73443 MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks)
73444 {
73445 if (pDelayNode == NULL) {
73446 return;
73447 }
73448
73449 /* The base node is always uninitialized first. */
73450 ma_node_uninit(pDelayNode, pAllocationCallbacks);
73451 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
73452 }
73453
73454 MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value)
73455 {
73456 if (pDelayNode == NULL) {
73457 return;
73458 }
73459
73460 ma_delay_set_wet(&pDelayNode->delay, value);
73461 }
73462
73463 MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode)
73464 {
73465 if (pDelayNode == NULL) {
73466 return 0;
73467 }
73468
73469 return ma_delay_get_wet(&pDelayNode->delay);
73470 }
73471
73472 MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value)
73473 {
73474 if (pDelayNode == NULL) {
73475 return;
73476 }
73477
73478 ma_delay_set_dry(&pDelayNode->delay, value);
73479 }
73480
73481 MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode)
73482 {
73483 if (pDelayNode == NULL) {
73484 return 0;
73485 }
73486
73487 return ma_delay_get_dry(&pDelayNode->delay);
73488 }
73489
73490 MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value)
73491 {
73492 if (pDelayNode == NULL) {
73493 return;
73494 }
73495
73496 ma_delay_set_decay(&pDelayNode->delay, value);
73497 }
73498
73499 MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode)
73500 {
73501 if (pDelayNode == NULL) {
73502 return 0;
73503 }
73504
73505 return ma_delay_get_decay(&pDelayNode->delay);
73506 }
73507 #endif /* MA_NO_NODE_GRAPH */
73508
73509
73510 /* SECTION: miniaudio_engine.c */
73511 #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
73512 /**************************************************************************************************************************************************************
73513
73514 Engine
73515
73516 **************************************************************************************************************************************************************/
73517 #define MA_SEEK_TARGET_NONE (~(ma_uint64)0)
73518
73519
73520 static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd)
73521 {
73522 MA_ASSERT(pSound != NULL);
73523 c89atomic_exchange_32(&pSound->atEnd, atEnd);
73524
73525 /* Fire any callbacks or events. */
73526 if (atEnd) {
73527 if (pSound->endCallback != NULL) {
73528 pSound->endCallback(pSound->pEndCallbackUserData, pSound);
73529 }
73530 }
73531 }
73532
73533 static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound)
73534 {
73535 MA_ASSERT(pSound != NULL);
73536 return c89atomic_load_32(&pSound->atEnd);
73537 }
73538
73539
73540 MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags)
73541 {
73542 ma_engine_node_config config;
73543
73544 MA_ZERO_OBJECT(&config);
73545 config.pEngine = pEngine;
73546 config.type = type;
73547 config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0;
73548 config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0;
73549 config.monoExpansionMode = pEngine->monoExpansionMode;
73550
73551 return config;
73552 }
73553
73554
73555 static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode)
73556 {
73557 ma_bool32 isUpdateRequired = MA_FALSE;
73558 float newPitch;
73559
73560 MA_ASSERT(pEngineNode != NULL);
73561
73562 newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire);
73563
73564 if (pEngineNode->oldPitch != newPitch) {
73565 pEngineNode->oldPitch = newPitch;
73566 isUpdateRequired = MA_TRUE;
73567 }
73568
73569 if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) {
73570 pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch;
73571 isUpdateRequired = MA_TRUE;
73572 }
73573
73574 if (isUpdateRequired) {
73575 float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine);
73576 ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch);
73577 }
73578 }
73579
73580 static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode)
73581 {
73582 MA_ASSERT(pEngineNode != NULL);
73583
73584 /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */
73585 return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire);
73586 }
73587
73588 static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode)
73589 {
73590 MA_ASSERT(pEngineNode != NULL);
73591
73592 return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire);
73593 }
73594
73595 static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount)
73596 {
73597 ma_uint64 inputFrameCount = 0;
73598
73599 if (ma_engine_node_is_pitching_enabled(pEngineNode)) {
73600 ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount);
73601 if (result != MA_SUCCESS) {
73602 inputFrameCount = 0;
73603 }
73604 } else {
73605 inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */
73606 }
73607
73608 return inputFrameCount;
73609 }
73610
73611 static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume)
73612 {
73613 if (pEngineNode == NULL) {
73614 return MA_INVALID_ARGS;
73615 }
73616
73617 ma_atomic_float_set(&pEngineNode->volume, volume);
73618
73619 /* If we're not smoothing we should bypass the volume gainer entirely. */
73620 if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) {
73621 /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */
73622 ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume);
73623 } else {
73624 /* We're using volume smoothing, so apply the master volume to the gainer. */
73625 ma_gainer_set_gain(&pEngineNode->volumeGainer, volume);
73626 }
73627
73628 return MA_SUCCESS;
73629 }
73630
73631 static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume)
73632 {
73633 if (pVolume == NULL) {
73634 return MA_INVALID_ARGS;
73635 }
73636
73637 *pVolume = 0.0f;
73638
73639 if (pEngineNode == NULL) {
73640 return MA_INVALID_ARGS;
73641 }
73642
73643 *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume);
73644
73645 return MA_SUCCESS;
73646 }
73647
73648
73649 static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73650 {
73651 ma_uint32 frameCountIn;
73652 ma_uint32 frameCountOut;
73653 ma_uint32 totalFramesProcessedIn;
73654 ma_uint32 totalFramesProcessedOut;
73655 ma_uint32 channelsIn;
73656 ma_uint32 channelsOut;
73657 ma_bool32 isPitchingEnabled;
73658 ma_bool32 isFadingEnabled;
73659 ma_bool32 isSpatializationEnabled;
73660 ma_bool32 isPanningEnabled;
73661 ma_bool32 isVolumeSmoothingEnabled;
73662
73663 frameCountIn = *pFrameCountIn;
73664 frameCountOut = *pFrameCountOut;
73665
73666 channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer);
73667 channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer);
73668
73669 totalFramesProcessedIn = 0;
73670 totalFramesProcessedOut = 0;
73671
73672 isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode);
73673 isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1;
73674 isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode);
73675 isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1;
73676 isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0;
73677
73678 /* Keep going while we've still got data available for processing. */
73679 while (totalFramesProcessedOut < frameCountOut) {
73680 /*
73681 We need to process in a specific order. We always do resampling first because it's likely
73682 we're going to be increasing the channel count after spatialization. Also, I want to do
73683 fading based on the output sample rate.
73684
73685 We'll first read into a buffer from the resampler. Then we'll do all processing that
73686 operates on the on the input channel count. We'll then get the spatializer to output to
73687 the output buffer and then do all effects from that point directly in the output buffer
73688 in-place.
73689
73690 Note that we're always running the resampler. If we try to be clever and skip resampling
73691 when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then
73692 away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler
73693 itself.
73694
73695 There's a small optimization here that we'll utilize since it might be a fairly common
73696 case. When the input and output channel counts are the same, we'll read straight into the
73697 output buffer from the resampler and do everything in-place.
73698 */
73699 const float* pRunningFramesIn;
73700 float* pRunningFramesOut;
73701 float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */
73702 float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
73703 ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn;
73704 ma_uint32 framesAvailableIn;
73705 ma_uint32 framesAvailableOut;
73706 ma_uint32 framesJustProcessedIn;
73707 ma_uint32 framesJustProcessedOut;
73708 ma_bool32 isWorkingBufferValid = MA_FALSE;
73709
73710 framesAvailableIn = frameCountIn - totalFramesProcessedIn;
73711 framesAvailableOut = frameCountOut - totalFramesProcessedOut;
73712
73713 pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn);
73714 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut);
73715
73716 if (channelsIn == channelsOut) {
73717 /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */
73718 pWorkingBuffer = pRunningFramesOut;
73719 } else {
73720 /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */
73721 pWorkingBuffer = temp;
73722 if (framesAvailableOut > tempCapInFrames) {
73723 framesAvailableOut = tempCapInFrames;
73724 }
73725 }
73726
73727 /* First is resampler. */
73728 if (isPitchingEnabled) {
73729 ma_uint64 resampleFrameCountIn = framesAvailableIn;
73730 ma_uint64 resampleFrameCountOut = framesAvailableOut;
73731
73732 ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut);
73733 isWorkingBufferValid = MA_TRUE;
73734
73735 framesJustProcessedIn = (ma_uint32)resampleFrameCountIn;
73736 framesJustProcessedOut = (ma_uint32)resampleFrameCountOut;
73737 } else {
73738 framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut);
73739 framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */
73740 }
73741
73742 /* Fading. */
73743 if (isFadingEnabled) {
73744 if (isWorkingBufferValid) {
73745 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */
73746 } else {
73747 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
73748 isWorkingBufferValid = MA_TRUE;
73749 }
73750 }
73751
73752 /*
73753 If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case
73754 we'll want to apply our volume now.
73755 */
73756 if (isVolumeSmoothingEnabled) {
73757 if (isWorkingBufferValid) {
73758 ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut);
73759 } else {
73760 ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
73761 isWorkingBufferValid = MA_TRUE;
73762 }
73763 }
73764
73765 /*
73766 If at this point we still haven't actually done anything with the working buffer we need
73767 to just read straight from the input buffer.
73768 */
73769 if (isWorkingBufferValid == MA_FALSE) {
73770 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. */
73771 }
73772
73773 /* Spatialization. */
73774 if (isSpatializationEnabled) {
73775 ma_uint32 iListener;
73776
73777 /*
73778 When determining the listener to use, we first check to see if the sound is pinned to a
73779 specific listener. If so, we use that. Otherwise we just use the closest listener.
73780 */
73781 if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) {
73782 iListener = pEngineNode->pinnedListenerIndex;
73783 } else {
73784 ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer);
73785 iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z);
73786 }
73787
73788 ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut);
73789 } else {
73790 /* No spatialization, but we still need to do channel conversion and master volume. */
73791 float volume;
73792 ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */
73793
73794 if (channelsIn == channelsOut) {
73795 /* No channel conversion required. Just copy straight to the output buffer. */
73796 if (isVolumeSmoothingEnabled) {
73797 /* Volume has already been applied. Just copy straight to the output buffer. */
73798 ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut);
73799 } else {
73800 /* Volume has not been applied yet. Copy and apply volume in the same pass. */
73801 ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume);
73802 }
73803 } else {
73804 /* Channel conversion required. TODO: Add support for channel maps here. */
73805 ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode);
73806
73807 /* If we're using smoothing, the volume will have already been applied. */
73808 if (!isVolumeSmoothingEnabled) {
73809 ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume);
73810 }
73811 }
73812 }
73813
73814 /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */
73815
73816 /* Panning. */
73817 if (isPanningEnabled) {
73818 ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */
73819 }
73820
73821 /* We're done for this chunk. */
73822 totalFramesProcessedIn += framesJustProcessedIn;
73823 totalFramesProcessedOut += framesJustProcessedOut;
73824
73825 /* 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. */
73826 if (framesJustProcessedOut == 0) {
73827 break;
73828 }
73829 }
73830
73831 /* At this point we're done processing. */
73832 *pFrameCountIn = totalFramesProcessedIn;
73833 *pFrameCountOut = totalFramesProcessedOut;
73834 }
73835
73836 static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73837 {
73838 /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
73839 ma_result result = MA_SUCCESS;
73840 ma_sound* pSound = (ma_sound*)pNode;
73841 ma_uint32 frameCount = *pFrameCountOut;
73842 ma_uint32 totalFramesRead = 0;
73843 ma_format dataSourceFormat;
73844 ma_uint32 dataSourceChannels;
73845 ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
73846 ma_uint32 tempCapInFrames;
73847 ma_uint64 seekTarget;
73848
73849 /* This is a data source node which means no input buses. */
73850 (void)ppFramesIn;
73851 (void)pFrameCountIn;
73852
73853 /* If we're marked at the end we need to stop the sound and do nothing. */
73854 if (ma_sound_at_end(pSound)) {
73855 ma_sound_stop(pSound);
73856 *pFrameCountOut = 0;
73857 return;
73858 }
73859
73860 /* If we're seeking, do so now before reading. */
73861 seekTarget = c89atomic_load_64(&pSound->seekTarget);
73862 if (seekTarget != MA_SEEK_TARGET_NONE) {
73863 ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget);
73864
73865 /* Any time-dependant effects need to have their times updated. */
73866 ma_node_set_time(pSound, seekTarget);
73867
73868 c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE);
73869 }
73870
73871 /*
73872 We want to update the pitch once. For sounds, this can be either at the start or at the end. If
73873 we don't force this to only ever be updating once, we could end up in a situation where
73874 retrieving the required input frame count ends up being different to what we actually retrieve.
73875 What could happen is that the required input frame count is calculated, the pitch is update,
73876 and then this processing function is called resulting in a different number of input frames
73877 being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else
73878 you'll hit the aforementioned bug.
73879 */
73880 ma_engine_node_update_pitch_if_required(&pSound->engineNode);
73881
73882 /*
73883 For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
73884 from the main engine.
73885 */
73886 result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0);
73887 if (result == MA_SUCCESS) {
73888 tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
73889
73890 /* Keep reading until we've read as much as was requested or we reach the end of the data source. */
73891 while (totalFramesRead < frameCount) {
73892 ma_uint32 framesRemaining = frameCount - totalFramesRead;
73893 ma_uint32 framesToRead;
73894 ma_uint64 framesJustRead;
73895 ma_uint32 frameCountIn;
73896 ma_uint32 frameCountOut;
73897 const float* pRunningFramesIn;
73898 float* pRunningFramesOut;
73899
73900 /*
73901 The first thing we need to do is read into the temporary buffer. We can calculate exactly
73902 how many input frames we'll need after resampling.
73903 */
73904 framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);
73905 if (framesToRead > tempCapInFrames) {
73906 framesToRead = tempCapInFrames;
73907 }
73908
73909 result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead);
73910
73911 /* 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. */
73912 if (result == MA_AT_END) {
73913 ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */
73914 }
73915
73916 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));
73917
73918 frameCountIn = (ma_uint32)framesJustRead;
73919 frameCountOut = framesRemaining;
73920
73921 /* Convert if necessary. */
73922 if (dataSourceFormat == ma_format_f32) {
73923 /* Fast path. No data conversion necessary. */
73924 pRunningFramesIn = (float*)temp;
73925 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
73926 } else {
73927 /* 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. */
73928 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. */
73929 ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
73930
73931 /* Now that we have our samples in f32 format we can process like normal. */
73932 pRunningFramesIn = tempf32;
73933 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
73934 }
73935
73936 /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */
73937 MA_ASSERT(frameCountIn == framesJustRead);
73938 totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
73939
73940 if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
73941 break; /* Might have reached the end. */
73942 }
73943 }
73944 }
73945
73946 *pFrameCountOut = totalFramesRead;
73947 }
73948
73949 static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73950 {
73951 /*
73952 Make sure the pitch is updated before trying to read anything. It's important that this is done
73953 only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that
73954 ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),
73955 and if another thread modifies the pitch just after that call it can result in a glitch due to
73956 the input rate changing.
73957 */
73958 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
73959
73960 /* For groups, the input data has already been read and we just need to apply the effect. */
73961 ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
73962 }
73963
73964 static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount)
73965 {
73966 ma_uint64 inputFrameCount;
73967
73968 MA_ASSERT(pInputFrameCount != NULL);
73969
73970 /* Our pitch will affect this calculation. We need to update it. */
73971 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
73972
73973 inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
73974 if (inputFrameCount > 0xFFFFFFFF) {
73975 inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
73976 }
73977
73978 *pInputFrameCount = (ma_uint32)inputFrameCount;
73979
73980 return MA_SUCCESS;
73981 }
73982
73983
73984 static ma_node_vtable g_ma_engine_node_vtable__sound =
73985 {
73986 ma_engine_node_process_pcm_frames__sound,
73987 NULL, /* onGetRequiredInputFrameCount */
73988 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
73989 1, /* Sounds have one output bus. */
73990 0 /* Default flags. */
73991 };
73992
73993 static ma_node_vtable g_ma_engine_node_vtable__group =
73994 {
73995 ma_engine_node_process_pcm_frames__group,
73996 ma_engine_node_get_required_input_frame_count__group,
73997 1, /* Groups have one input bus. */
73998 1, /* Groups have one output bus. */
73999 MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
74000 };
74001
74002
74003
74004 static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)
74005 {
74006 ma_node_config baseNodeConfig;
74007
74008 if (pConfig->type == ma_engine_node_type_sound) {
74009 /* Sound. */
74010 baseNodeConfig = ma_node_config_init();
74011 baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound;
74012 baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */
74013 } else {
74014 /* Group. */
74015 baseNodeConfig = ma_node_config_init();
74016 baseNodeConfig.vtable = &g_ma_engine_node_vtable__group;
74017 baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */
74018 }
74019
74020 return baseNodeConfig;
74021 }
74022
74023 static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig)
74024 {
74025 return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]);
74026 }
74027
74028 typedef struct
74029 {
74030 size_t sizeInBytes;
74031 size_t baseNodeOffset;
74032 size_t resamplerOffset;
74033 size_t spatializerOffset;
74034 size_t gainerOffset;
74035 } ma_engine_node_heap_layout;
74036
74037 static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout)
74038 {
74039 ma_result result;
74040 size_t tempHeapSize;
74041 ma_node_config baseNodeConfig;
74042 ma_linear_resampler_config resamplerConfig;
74043 ma_spatializer_config spatializerConfig;
74044 ma_gainer_config gainerConfig;
74045 ma_uint32 channelsIn;
74046 ma_uint32 channelsOut;
74047 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. */
74048
74049 MA_ASSERT(pHeapLayout);
74050
74051 MA_ZERO_OBJECT(pHeapLayout);
74052
74053 if (pConfig == NULL) {
74054 return MA_INVALID_ARGS;
74055 }
74056
74057 if (pConfig->pEngine == NULL) {
74058 return MA_INVALID_ARGS; /* An engine must be specified. */
74059 }
74060
74061 pHeapLayout->sizeInBytes = 0;
74062
74063 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
74064 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
74065
74066
74067 /* Base node. */
74068 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
74069 baseNodeConfig.pInputChannels = &channelsIn;
74070 baseNodeConfig.pOutputChannels = &channelsOut;
74071
74072 result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize);
74073 if (result != MA_SUCCESS) {
74074 return result; /* Failed to retrieve the size of the heap for the base node. */
74075 }
74076
74077 pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;
74078 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74079
74080
74081 /* Resmapler. */
74082 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. */
74083 resamplerConfig.lpfOrder = 0;
74084
74085 result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize);
74086 if (result != MA_SUCCESS) {
74087 return result; /* Failed to retrieve the size of the heap for the resampler. */
74088 }
74089
74090 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
74091 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74092
74093
74094 /* Spatializer. */
74095 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
74096
74097 if (spatializerConfig.channelsIn == 2) {
74098 spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
74099 }
74100
74101 result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize);
74102 if (result != MA_SUCCESS) {
74103 return result; /* Failed to retrieve the size of the heap for the spatializer. */
74104 }
74105
74106 pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes;
74107 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74108
74109
74110 /* Gainer. Will not be used if we are not using smoothing. */
74111 if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
74112 gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
74113
74114 result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize);
74115 if (result != MA_SUCCESS) {
74116 return result;
74117 }
74118
74119 pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
74120 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
74121 }
74122
74123
74124 return MA_SUCCESS;
74125 }
74126
74127 MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes)
74128 {
74129 ma_result result;
74130 ma_engine_node_heap_layout heapLayout;
74131
74132 if (pHeapSizeInBytes == NULL) {
74133 return MA_INVALID_ARGS;
74134 }
74135
74136 *pHeapSizeInBytes = 0;
74137
74138 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
74139 if (result != MA_SUCCESS) {
74140 return result;
74141 }
74142
74143 *pHeapSizeInBytes = heapLayout.sizeInBytes;
74144
74145 return MA_SUCCESS;
74146 }
74147
74148 MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode)
74149 {
74150 ma_result result;
74151 ma_engine_node_heap_layout heapLayout;
74152 ma_node_config baseNodeConfig;
74153 ma_linear_resampler_config resamplerConfig;
74154 ma_fader_config faderConfig;
74155 ma_spatializer_config spatializerConfig;
74156 ma_panner_config pannerConfig;
74157 ma_gainer_config gainerConfig;
74158 ma_uint32 channelsIn;
74159 ma_uint32 channelsOut;
74160 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. */
74161
74162 if (pEngineNode == NULL) {
74163 return MA_INVALID_ARGS;
74164 }
74165
74166 MA_ZERO_OBJECT(pEngineNode);
74167
74168 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
74169 if (result != MA_SUCCESS) {
74170 return result;
74171 }
74172
74173 if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) {
74174 return MA_INVALID_ARGS; /* Invalid listener. */
74175 }
74176
74177 pEngineNode->_pHeap = pHeap;
74178 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
74179
74180 pEngineNode->pEngine = pConfig->pEngine;
74181 pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine);
74182 pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;
74183 pEngineNode->monoExpansionMode = pConfig->monoExpansionMode;
74184 ma_atomic_float_set(&pEngineNode->volume, 1);
74185 pEngineNode->pitch = 1;
74186 pEngineNode->oldPitch = 1;
74187 pEngineNode->oldDopplerPitch = 1;
74188 pEngineNode->isPitchDisabled = pConfig->isPitchDisabled;
74189 pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled;
74190 pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex;
74191
74192 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
74193 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
74194
74195 /*
74196 If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler
74197 is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used.
74198 */
74199 if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) {
74200 pEngineNode->isPitchDisabled = MA_FALSE;
74201 }
74202
74203
74204 /* Base node. */
74205 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
74206 baseNodeConfig.pInputChannels = &channelsIn;
74207 baseNodeConfig.pOutputChannels = &channelsOut;
74208
74209 result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);
74210 if (result != MA_SUCCESS) {
74211 goto error0;
74212 }
74213
74214
74215 /*
74216 We can now initialize the effects we need in order to implement the engine node. There's a
74217 defined order of operations here, mainly centered around when we convert our channels from the
74218 data source's native channel count to the engine's channel count. As a rule, we want to do as
74219 much computation as possible before spatialization because there's a chance that will increase
74220 the channel count, thereby increasing the amount of work needing to be done to process.
74221 */
74222
74223 /* We'll always do resampling first. */
74224 resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine));
74225 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. */
74226
74227 result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler);
74228 if (result != MA_SUCCESS) {
74229 goto error1;
74230 }
74231
74232
74233 /* After resampling will come the fader. */
74234 faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine));
74235
74236 result = ma_fader_init(&faderConfig, &pEngineNode->fader);
74237 if (result != MA_SUCCESS) {
74238 goto error2;
74239 }
74240
74241
74242 /*
74243 Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to
74244 ensure channels counts link up correctly in the node graph.
74245 */
74246 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
74247 spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames;
74248
74249 if (spatializerConfig.channelsIn == 2) {
74250 spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
74251 }
74252
74253 result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer);
74254 if (result != MA_SUCCESS) {
74255 goto error2;
74256 }
74257
74258
74259 /*
74260 After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't
74261 be able to pan mono sounds.
74262 */
74263 pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]);
74264
74265 result = ma_panner_init(&pannerConfig, &pEngineNode->panner);
74266 if (result != MA_SUCCESS) {
74267 goto error3;
74268 }
74269
74270
74271 /* 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. */
74272 if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
74273 gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
74274
74275 result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer);
74276 if (result != MA_SUCCESS) {
74277 goto error3;
74278 }
74279 }
74280
74281
74282 return MA_SUCCESS;
74283
74284 /* No need for allocation callbacks here because we use a preallocated heap. */
74285 error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL);
74286 error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL);
74287 error1: ma_node_uninit(&pEngineNode->baseNode, NULL);
74288 error0: return result;
74289 }
74290
74291 MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
74292 {
74293 ma_result result;
74294 size_t heapSizeInBytes;
74295 void* pHeap;
74296
74297 result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes);
74298 if (result != MA_SUCCESS) {
74299 return result;
74300 }
74301
74302 if (heapSizeInBytes > 0) {
74303 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
74304 if (pHeap == NULL) {
74305 return MA_OUT_OF_MEMORY;
74306 }
74307 } else {
74308 pHeap = NULL;
74309 }
74310
74311 result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode);
74312 if (result != MA_SUCCESS) {
74313 ma_free(pHeap, pAllocationCallbacks);
74314 return result;
74315 }
74316
74317 pEngineNode->_ownsHeap = MA_TRUE;
74318 return MA_SUCCESS;
74319 }
74320
74321 MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks)
74322 {
74323 /*
74324 The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we
74325 destroy anything that might be in the middle of being used by the processing function.
74326 */
74327 ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks);
74328
74329 /* Now that the node has been uninitialized we can safely uninitialize the rest. */
74330 if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) {
74331 ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks);
74332 }
74333
74334 ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks);
74335 ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks);
74336
74337 /* Free the heap last. */
74338 if (pEngineNode->_ownsHeap) {
74339 ma_free(pEngineNode->_pHeap, pAllocationCallbacks);
74340 }
74341 }
74342
74343
74344 MA_API ma_sound_config ma_sound_config_init(void)
74345 {
74346 return ma_sound_config_init_2(NULL);
74347 }
74348
74349 MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine)
74350 {
74351 ma_sound_config config;
74352
74353 MA_ZERO_OBJECT(&config);
74354
74355 if (pEngine != NULL) {
74356 config.monoExpansionMode = pEngine->monoExpansionMode;
74357 } else {
74358 config.monoExpansionMode = ma_mono_expansion_mode_default;
74359 }
74360
74361 config.rangeEndInPCMFrames = ~((ma_uint64)0);
74362 config.loopPointEndInPCMFrames = ~((ma_uint64)0);
74363
74364 return config;
74365 }
74366
74367 MA_API ma_sound_group_config ma_sound_group_config_init(void)
74368 {
74369 return ma_sound_group_config_init_2(NULL);
74370 }
74371
74372 MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine)
74373 {
74374 ma_sound_group_config config;
74375
74376 MA_ZERO_OBJECT(&config);
74377
74378 if (pEngine != NULL) {
74379 config.monoExpansionMode = pEngine->monoExpansionMode;
74380 } else {
74381 config.monoExpansionMode = ma_mono_expansion_mode_default;
74382 }
74383
74384 return config;
74385 }
74386
74387
74388 MA_API ma_engine_config ma_engine_config_init(void)
74389 {
74390 ma_engine_config config;
74391
74392 MA_ZERO_OBJECT(&config);
74393 config.listenerCount = 1; /* Always want at least one listener. */
74394 config.monoExpansionMode = ma_mono_expansion_mode_default;
74395
74396 return config;
74397 }
74398
74399
74400 #if !defined(MA_NO_DEVICE_IO)
74401 static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
74402 {
74403 ma_engine* pEngine = (ma_engine*)pDevice->pUserData;
74404
74405 (void)pFramesIn;
74406
74407 /*
74408 Experiment: Try processing a resource manager job if we're on the Emscripten build.
74409
74410 This serves two purposes:
74411
74412 1) It ensures jobs are actually processed at some point since we cannot guarantee that the
74413 caller is doing the right thing and calling ma_resource_manager_process_next_job(); and
74414
74415 2) It's an attempt at working around an issue where processing jobs on the Emscripten main
74416 loop doesn't work as well as it should. When trying to load sounds without the `DECODE`
74417 flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time
74418 before the callback is processed. I think it's got something to do with the single-
74419 threaded nature of Web, but I'm not entirely sure.
74420 */
74421 #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)
74422 {
74423 if (pEngine->pResourceManager != NULL) {
74424 if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
74425 ma_resource_manager_process_next_job(pEngine->pResourceManager);
74426 }
74427 }
74428 }
74429 #endif
74430
74431 ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);
74432 }
74433 #endif
74434
74435 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)
74436 {
74437 ma_result result;
74438 ma_node_graph_config nodeGraphConfig;
74439 ma_engine_config engineConfig;
74440 ma_spatializer_listener_config listenerConfig;
74441 ma_uint32 iListener;
74442
74443 if (pEngine == NULL) {
74444 return MA_INVALID_ARGS;
74445 }
74446
74447 MA_ZERO_OBJECT(pEngine);
74448
74449 /* The config is allowed to be NULL in which case we use defaults for everything. */
74450 if (pConfig != NULL) {
74451 engineConfig = *pConfig;
74452 } else {
74453 engineConfig = ma_engine_config_init();
74454 }
74455
74456 pEngine->monoExpansionMode = engineConfig.monoExpansionMode;
74457 pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames;
74458 ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks);
74459
74460 #if !defined(MA_NO_RESOURCE_MANAGER)
74461 {
74462 pEngine->pResourceManager = engineConfig.pResourceManager;
74463 }
74464 #endif
74465
74466 #if !defined(MA_NO_DEVICE_IO)
74467 {
74468 pEngine->pDevice = engineConfig.pDevice;
74469
74470 /* If we don't have a device, we need one. */
74471 if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) {
74472 ma_device_config deviceConfig;
74473
74474 pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks);
74475 if (pEngine->pDevice == NULL) {
74476 return MA_OUT_OF_MEMORY;
74477 }
74478
74479 deviceConfig = ma_device_config_init(ma_device_type_playback);
74480 deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID;
74481 deviceConfig.playback.format = ma_format_f32;
74482 deviceConfig.playback.channels = engineConfig.channels;
74483 deviceConfig.sampleRate = engineConfig.sampleRate;
74484 deviceConfig.dataCallback = ma_engine_data_callback_internal;
74485 deviceConfig.pUserData = pEngine;
74486 deviceConfig.notificationCallback = engineConfig.notificationCallback;
74487 deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames;
74488 deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds;
74489 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. */
74490 deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */
74491
74492 if (engineConfig.pContext == NULL) {
74493 ma_context_config contextConfig = ma_context_config_init();
74494 contextConfig.allocationCallbacks = pEngine->allocationCallbacks;
74495 contextConfig.pLog = engineConfig.pLog;
74496
74497 /* If the engine config does not specify a log, use the resource manager's if we have one. */
74498 #ifndef MA_NO_RESOURCE_MANAGER
74499 {
74500 if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) {
74501 contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager);
74502 }
74503 }
74504 #endif
74505
74506 result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice);
74507 } else {
74508 result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice);
74509 }
74510
74511 if (result != MA_SUCCESS) {
74512 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
74513 pEngine->pDevice = NULL;
74514 return result;
74515 }
74516
74517 pEngine->ownsDevice = MA_TRUE;
74518 }
74519
74520 /* Update the channel count and sample rate of the engine config so we can reference it below. */
74521 if (pEngine->pDevice != NULL) {
74522 engineConfig.channels = pEngine->pDevice->playback.channels;
74523 engineConfig.sampleRate = pEngine->pDevice->sampleRate;
74524 }
74525 }
74526 #endif
74527
74528 if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) {
74529 return MA_INVALID_ARGS;
74530 }
74531
74532 pEngine->sampleRate = engineConfig.sampleRate;
74533
74534 /* The engine always uses either the log that was passed into the config, or the context's log is available. */
74535 if (engineConfig.pLog != NULL) {
74536 pEngine->pLog = engineConfig.pLog;
74537 } else {
74538 #if !defined(MA_NO_DEVICE_IO)
74539 {
74540 pEngine->pLog = ma_device_get_log(pEngine->pDevice);
74541 }
74542 #else
74543 {
74544 pEngine->pLog = NULL;
74545 }
74546 #endif
74547 }
74548
74549
74550 /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */
74551 nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels);
74552 nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames;
74553
74554 result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);
74555 if (result != MA_SUCCESS) {
74556 goto on_error_1;
74557 }
74558
74559
74560 /* We need at least one listener. */
74561 if (engineConfig.listenerCount == 0) {
74562 engineConfig.listenerCount = 1;
74563 }
74564
74565 if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) {
74566 result = MA_INVALID_ARGS; /* Too many listeners. */
74567 goto on_error_1;
74568 }
74569
74570 for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) {
74571 listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph));
74572
74573 /*
74574 If we're using a device, use the device's channel map for the listener. Otherwise just use
74575 miniaudio's default channel map.
74576 */
74577 #if !defined(MA_NO_DEVICE_IO)
74578 {
74579 if (pEngine->pDevice != NULL) {
74580 /*
74581 Temporarily disabled. There is a subtle bug here where front-left and front-right
74582 will be used by the device's channel map, but this is not what we want to use for
74583 spatialization. Instead we want to use side-left and side-right. I need to figure
74584 out a better solution for this. For now, disabling the use of device channel maps.
74585 */
74586 /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/
74587 }
74588 }
74589 #endif
74590
74591 result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */
74592 if (result != MA_SUCCESS) {
74593 goto on_error_2;
74594 }
74595
74596 pEngine->listenerCount += 1;
74597 }
74598
74599
74600 /* Gain smoothing for spatialized sounds. */
74601 pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;
74602 if (pEngine->gainSmoothTimeInFrames == 0) {
74603 ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;
74604 if (gainSmoothTimeInMilliseconds == 0) {
74605 gainSmoothTimeInMilliseconds = 8;
74606 }
74607
74608 pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */
74609 }
74610
74611
74612 /* We need a resource manager. */
74613 #ifndef MA_NO_RESOURCE_MANAGER
74614 {
74615 if (pEngine->pResourceManager == NULL) {
74616 ma_resource_manager_config resourceManagerConfig;
74617
74618 pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);
74619 if (pEngine->pResourceManager == NULL) {
74620 result = MA_OUT_OF_MEMORY;
74621 goto on_error_2;
74622 }
74623
74624 resourceManagerConfig = ma_resource_manager_config_init();
74625 resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */
74626 resourceManagerConfig.decodedFormat = ma_format_f32;
74627 resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */
74628 resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);
74629 ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);
74630 resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS;
74631
74632 /* The Emscripten build cannot use threads. */
74633 #if defined(MA_EMSCRIPTEN)
74634 {
74635 resourceManagerConfig.jobThreadCount = 0;
74636 resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
74637 }
74638 #endif
74639
74640 result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);
74641 if (result != MA_SUCCESS) {
74642 goto on_error_3;
74643 }
74644
74645 pEngine->ownsResourceManager = MA_TRUE;
74646 }
74647 }
74648 #endif
74649
74650 /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */
74651 pEngine->inlinedSoundLock = 0;
74652 pEngine->pInlinedSoundHead = NULL;
74653
74654 /* Start the engine if required. This should always be the last step. */
74655 #if !defined(MA_NO_DEVICE_IO)
74656 {
74657 if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) {
74658 result = ma_engine_start(pEngine);
74659 if (result != MA_SUCCESS) {
74660 goto on_error_4; /* Failed to start the engine. */
74661 }
74662 }
74663 }
74664 #endif
74665
74666 return MA_SUCCESS;
74667
74668 #if !defined(MA_NO_DEVICE_IO)
74669 on_error_4:
74670 #endif
74671 #if !defined(MA_NO_RESOURCE_MANAGER)
74672 on_error_3:
74673 if (pEngine->ownsResourceManager) {
74674 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
74675 }
74676 #endif /* MA_NO_RESOURCE_MANAGER */
74677 on_error_2:
74678 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
74679 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
74680 }
74681
74682 ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
74683 on_error_1:
74684 #if !defined(MA_NO_DEVICE_IO)
74685 {
74686 if (pEngine->ownsDevice) {
74687 ma_device_uninit(pEngine->pDevice);
74688 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
74689 }
74690 }
74691 #endif
74692
74693 return result;
74694 }
74695
74696 MA_API void ma_engine_uninit(ma_engine* pEngine)
74697 {
74698 ma_uint32 iListener;
74699
74700 if (pEngine == NULL) {
74701 return;
74702 }
74703
74704 /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */
74705 #if !defined(MA_NO_DEVICE_IO)
74706 {
74707 if (pEngine->ownsDevice) {
74708 ma_device_uninit(pEngine->pDevice);
74709 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
74710 } else {
74711 if (pEngine->pDevice != NULL) {
74712 ma_device_stop(pEngine->pDevice);
74713 }
74714 }
74715 }
74716 #endif
74717
74718 /*
74719 All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case
74720 I want to do some kind of garbage collection later on.
74721 */
74722 ma_spinlock_lock(&pEngine->inlinedSoundLock);
74723 {
74724 for (;;) {
74725 ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;
74726 if (pSoundToDelete == NULL) {
74727 break; /* Done. */
74728 }
74729
74730 pEngine->pInlinedSoundHead = pSoundToDelete->pNext;
74731
74732 ma_sound_uninit(&pSoundToDelete->sound);
74733 ma_free(pSoundToDelete, &pEngine->allocationCallbacks);
74734 }
74735 }
74736 ma_spinlock_unlock(&pEngine->inlinedSoundLock);
74737
74738 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
74739 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
74740 }
74741
74742 /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */
74743 ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
74744
74745 /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */
74746 #ifndef MA_NO_RESOURCE_MANAGER
74747 if (pEngine->ownsResourceManager) {
74748 ma_resource_manager_uninit(pEngine->pResourceManager);
74749 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
74750 }
74751 #endif
74752 }
74753
74754 MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
74755 {
74756 return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead);
74757 }
74758
74759 MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine)
74760 {
74761 if (pEngine == NULL) {
74762 return NULL;
74763 }
74764
74765 return &pEngine->nodeGraph;
74766 }
74767
74768 #if !defined(MA_NO_RESOURCE_MANAGER)
74769 MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine)
74770 {
74771 if (pEngine == NULL) {
74772 return NULL;
74773 }
74774
74775 #if !defined(MA_NO_RESOURCE_MANAGER)
74776 {
74777 return pEngine->pResourceManager;
74778 }
74779 #else
74780 {
74781 return NULL;
74782 }
74783 #endif
74784 }
74785 #endif
74786
74787 MA_API ma_device* ma_engine_get_device(ma_engine* pEngine)
74788 {
74789 if (pEngine == NULL) {
74790 return NULL;
74791 }
74792
74793 #if !defined(MA_NO_DEVICE_IO)
74794 {
74795 return pEngine->pDevice;
74796 }
74797 #else
74798 {
74799 return NULL;
74800 }
74801 #endif
74802 }
74803
74804 MA_API ma_log* ma_engine_get_log(ma_engine* pEngine)
74805 {
74806 if (pEngine == NULL) {
74807 return NULL;
74808 }
74809
74810 if (pEngine->pLog != NULL) {
74811 return pEngine->pLog;
74812 } else {
74813 #if !defined(MA_NO_DEVICE_IO)
74814 {
74815 return ma_device_get_log(ma_engine_get_device(pEngine));
74816 }
74817 #else
74818 {
74819 return NULL;
74820 }
74821 #endif
74822 }
74823 }
74824
74825 MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine)
74826 {
74827 return ma_node_graph_get_endpoint(&pEngine->nodeGraph);
74828 }
74829
74830 MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine)
74831 {
74832 return ma_node_graph_get_time(&pEngine->nodeGraph);
74833 }
74834
74835 MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine)
74836 {
74837 return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine);
74838 }
74839
74840 MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime)
74841 {
74842 return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime);
74843 }
74844
74845 MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime)
74846 {
74847 return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000);
74848 }
74849
74850 MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine)
74851 {
74852 return ma_engine_get_time_in_pcm_frames(pEngine);
74853 }
74854
74855 MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime)
74856 {
74857 return ma_engine_set_time_in_pcm_frames(pEngine, globalTime);
74858 }
74859
74860 MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine)
74861 {
74862 return ma_node_graph_get_channels(&pEngine->nodeGraph);
74863 }
74864
74865 MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine)
74866 {
74867 if (pEngine == NULL) {
74868 return 0;
74869 }
74870
74871 return pEngine->sampleRate;
74872 }
74873
74874
74875 MA_API ma_result ma_engine_start(ma_engine* pEngine)
74876 {
74877 ma_result result;
74878
74879 if (pEngine == NULL) {
74880 return MA_INVALID_ARGS;
74881 }
74882
74883 #if !defined(MA_NO_DEVICE_IO)
74884 {
74885 if (pEngine->pDevice != NULL) {
74886 result = ma_device_start(pEngine->pDevice);
74887 } else {
74888 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */
74889 }
74890 }
74891 #else
74892 {
74893 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */
74894 }
74895 #endif
74896
74897 if (result != MA_SUCCESS) {
74898 return result;
74899 }
74900
74901 return MA_SUCCESS;
74902 }
74903
74904 MA_API ma_result ma_engine_stop(ma_engine* pEngine)
74905 {
74906 ma_result result;
74907
74908 if (pEngine == NULL) {
74909 return MA_INVALID_ARGS;
74910 }
74911
74912 #if !defined(MA_NO_DEVICE_IO)
74913 {
74914 if (pEngine->pDevice != NULL) {
74915 result = ma_device_stop(pEngine->pDevice);
74916 } else {
74917 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */
74918 }
74919 }
74920 #else
74921 {
74922 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */
74923 }
74924 #endif
74925
74926 if (result != MA_SUCCESS) {
74927 return result;
74928 }
74929
74930 return MA_SUCCESS;
74931 }
74932
74933 MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume)
74934 {
74935 if (pEngine == NULL) {
74936 return MA_INVALID_ARGS;
74937 }
74938
74939 return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume);
74940 }
74941
74942 MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB)
74943 {
74944 if (pEngine == NULL) {
74945 return MA_INVALID_ARGS;
74946 }
74947
74948 return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, ma_volume_db_to_linear(gainDB));
74949 }
74950
74951
74952 MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine)
74953 {
74954 if (pEngine == NULL) {
74955 return 0;
74956 }
74957
74958 return pEngine->listenerCount;
74959 }
74960
74961 MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
74962 {
74963 ma_uint32 iListener;
74964 ma_uint32 iListenerClosest;
74965 float closestLen2 = MA_FLT_MAX;
74966
74967 if (pEngine == NULL || pEngine->listenerCount == 1) {
74968 return 0;
74969 }
74970
74971 iListenerClosest = 0;
74972 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
74973 if (ma_engine_listener_is_enabled(pEngine, iListener)) {
74974 float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ)));
74975 if (closestLen2 > len2) {
74976 closestLen2 = len2;
74977 iListenerClosest = iListener;
74978 }
74979 }
74980 }
74981
74982 MA_ASSERT(iListenerClosest < 255);
74983 return iListenerClosest;
74984 }
74985
74986 MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
74987 {
74988 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
74989 return;
74990 }
74991
74992 ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z);
74993 }
74994
74995 MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex)
74996 {
74997 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
74998 return ma_vec3f_init_3f(0, 0, 0);
74999 }
75000
75001 return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]);
75002 }
75003
75004 MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
75005 {
75006 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75007 return;
75008 }
75009
75010 ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z);
75011 }
75012
75013 MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex)
75014 {
75015 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75016 return ma_vec3f_init_3f(0, 0, -1);
75017 }
75018
75019 return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]);
75020 }
75021
75022 MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
75023 {
75024 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75025 return;
75026 }
75027
75028 ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z);
75029 }
75030
75031 MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex)
75032 {
75033 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75034 return ma_vec3f_init_3f(0, 0, 0);
75035 }
75036
75037 return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]);
75038 }
75039
75040 MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
75041 {
75042 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75043 return;
75044 }
75045
75046 ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain);
75047 }
75048
75049 MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
75050 {
75051 if (pInnerAngleInRadians != NULL) {
75052 *pInnerAngleInRadians = 0;
75053 }
75054
75055 if (pOuterAngleInRadians != NULL) {
75056 *pOuterAngleInRadians = 0;
75057 }
75058
75059 if (pOuterGain != NULL) {
75060 *pOuterGain = 0;
75061 }
75062
75063 ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
75064 }
75065
75066 MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
75067 {
75068 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75069 return;
75070 }
75071
75072 ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z);
75073 }
75074
75075 MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex)
75076 {
75077 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75078 return ma_vec3f_init_3f(0, 1, 0);
75079 }
75080
75081 return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]);
75082 }
75083
75084 MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled)
75085 {
75086 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75087 return;
75088 }
75089
75090 ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled);
75091 }
75092
75093 MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex)
75094 {
75095 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
75096 return MA_FALSE;
75097 }
75098
75099 return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]);
75100 }
75101
75102
75103 #ifndef MA_NO_RESOURCE_MANAGER
75104 MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex)
75105 {
75106 ma_result result = MA_SUCCESS;
75107 ma_sound_inlined* pSound = NULL;
75108 ma_sound_inlined* pNextSound = NULL;
75109
75110 if (pEngine == NULL || pFilePath == NULL) {
75111 return MA_INVALID_ARGS;
75112 }
75113
75114 /* Attach to the endpoint node if nothing is specicied. */
75115 if (pNode == NULL) {
75116 pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);
75117 nodeInputBusIndex = 0;
75118 }
75119
75120 /*
75121 We want to check if we can recycle an already-allocated inlined sound. Since this is just a
75122 helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep
75123 the implementation simple. Maybe this can be optimized later if there's enough demand, but
75124 if this function is being used it probably means the caller doesn't really care too much.
75125
75126 What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise
75127 we just keep iterating. If we reach the end without finding a sound to recycle we just
75128 allocate a new one. This doesn't scale well for a massive number of sounds being played
75129 simultaneously as we don't ever actually free the sound objects. Some kind of garbage
75130 collection routine might be valuable for this which I'll think about.
75131 */
75132 ma_spinlock_lock(&pEngine->inlinedSoundLock);
75133 {
75134 ma_uint32 soundFlags = 0;
75135
75136 for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) {
75137 if (ma_sound_at_end(&pNextSound->sound)) {
75138 /*
75139 The sound is at the end which means it's available for recycling. All we need to do
75140 is uninitialize it and reinitialize it. All we're doing is recycling memory.
75141 */
75142 pSound = pNextSound;
75143 c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1);
75144 break;
75145 }
75146 }
75147
75148 if (pSound != NULL) {
75149 /*
75150 We actually want to detach the sound from the list here. The reason is because we want the sound
75151 to be in a consistent state at the non-recycled case to simplify the logic below.
75152 */
75153 if (pEngine->pInlinedSoundHead == pSound) {
75154 pEngine->pInlinedSoundHead = pSound->pNext;
75155 }
75156
75157 if (pSound->pPrev != NULL) {
75158 pSound->pPrev->pNext = pSound->pNext;
75159 }
75160 if (pSound->pNext != NULL) {
75161 pSound->pNext->pPrev = pSound->pPrev;
75162 }
75163
75164 /* Now the previous sound needs to be uninitialized. */
75165 ma_sound_uninit(&pNextSound->sound);
75166 } else {
75167 /* No sound available for recycling. Allocate one now. */
75168 pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks);
75169 }
75170
75171 if (pSound != NULL) { /* Safety check for the allocation above. */
75172 /*
75173 At this point we should have memory allocated for the inlined sound. We just need
75174 to initialize it like a normal sound now.
75175 */
75176 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. */
75177 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. */
75178 soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */
75179 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. */
75180
75181 result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);
75182 if (result == MA_SUCCESS) {
75183 /* Now attach the sound to the graph. */
75184 result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);
75185 if (result == MA_SUCCESS) {
75186 /* 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. */
75187 pSound->pNext = pEngine->pInlinedSoundHead;
75188 pSound->pPrev = NULL;
75189
75190 pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */
75191 if (pSound->pNext != NULL) {
75192 pSound->pNext->pPrev = pSound;
75193 }
75194 } else {
75195 ma_free(pSound, &pEngine->allocationCallbacks);
75196 }
75197 } else {
75198 ma_free(pSound, &pEngine->allocationCallbacks);
75199 }
75200 } else {
75201 result = MA_OUT_OF_MEMORY;
75202 }
75203 }
75204 ma_spinlock_unlock(&pEngine->inlinedSoundLock);
75205
75206 if (result != MA_SUCCESS) {
75207 return result;
75208 }
75209
75210 /* Finally we can start playing the sound. */
75211 result = ma_sound_start(&pSound->sound);
75212 if (result != MA_SUCCESS) {
75213 /* Failed to start the sound. We need to mark it for recycling and return an error. */
75214 c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE);
75215 return result;
75216 }
75217
75218 c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1);
75219 return result;
75220 }
75221
75222 MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
75223 {
75224 return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0);
75225 }
75226 #endif
75227
75228
75229 static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound)
75230 {
75231 if (pSound == NULL) {
75232 return MA_INVALID_ARGS;
75233 }
75234
75235 MA_ZERO_OBJECT(pSound);
75236 pSound->seekTarget = MA_SEEK_TARGET_NONE;
75237
75238 if (pEngine == NULL) {
75239 return MA_INVALID_ARGS;
75240 }
75241
75242 return MA_SUCCESS;
75243 }
75244
75245 static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
75246 {
75247 ma_result result;
75248 ma_engine_node_config engineNodeConfig;
75249 ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */
75250
75251 /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */
75252 MA_ASSERT(pEngine != NULL);
75253 MA_ASSERT(pSound != NULL);
75254
75255 if (pConfig == NULL) {
75256 return MA_INVALID_ARGS;
75257 }
75258
75259 pSound->pDataSource = pConfig->pDataSource;
75260
75261 if (pConfig->pDataSource != NULL) {
75262 type = ma_engine_node_type_sound;
75263 } else {
75264 type = ma_engine_node_type_group;
75265 }
75266
75267 /*
75268 Sounds are engine nodes. Before we can initialize this we need to determine the channel count.
75269 If we can't do this we need to abort. It's up to the caller to ensure they're using a data
75270 source that provides this information upfront.
75271 */
75272 engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags);
75273 engineNodeConfig.channelsIn = pConfig->channelsIn;
75274 engineNodeConfig.channelsOut = pConfig->channelsOut;
75275 engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;
75276 engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode;
75277
75278 if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) {
75279 engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames;
75280 }
75281
75282 /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */
75283 if (pConfig->pDataSource != NULL) {
75284 result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0);
75285 if (result != MA_SUCCESS) {
75286 return result; /* Failed to retrieve the channel count. */
75287 }
75288
75289 if (engineNodeConfig.channelsIn == 0) {
75290 return MA_INVALID_OPERATION; /* Invalid channel count. */
75291 }
75292
75293 if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) {
75294 engineNodeConfig.channelsOut = engineNodeConfig.channelsIn;
75295 }
75296 }
75297
75298
75299 /* Getting here means we should have a valid channel count and we can initialize the engine node. */
75300 result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode);
75301 if (result != MA_SUCCESS) {
75302 return result;
75303 }
75304
75305 /* If no attachment is specified, attach the sound straight to the endpoint. */
75306 if (pConfig->pInitialAttachment == NULL) {
75307 /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */
75308 if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) {
75309 result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
75310 }
75311 } else {
75312 /* 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. */
75313 result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex);
75314 }
75315
75316 if (result != MA_SUCCESS) {
75317 ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks);
75318 return result;
75319 }
75320
75321
75322 /* Apply initial range and looping state to the data source if applicable. */
75323 if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) {
75324 ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
75325 }
75326
75327 if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) {
75328 ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
75329 }
75330
75331 ma_sound_set_looping(pSound, pConfig->isLooping);
75332
75333 return MA_SUCCESS;
75334 }
75335
75336 #ifndef MA_NO_RESOURCE_MANAGER
75337 MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
75338 {
75339 ma_result result = MA_SUCCESS;
75340 ma_uint32 flags;
75341 ma_sound_config config;
75342 ma_resource_manager_pipeline_notifications notifications;
75343
75344 /*
75345 The engine requires knowledge of the channel count of the underlying data source before it can
75346 initialize the sound. Therefore, we need to make the resource manager wait until initialization
75347 of the underlying data source to be initialized so we can get access to the channel count. To
75348 do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced.
75349
75350 Because we're initializing the data source before the sound, there's a chance the notification
75351 will get triggered before this function returns. This is OK, so long as the caller is aware of
75352 it and can avoid accessing the sound from within the notification.
75353 */
75354 flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT;
75355
75356 pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);
75357 if (pSound->pResourceManagerDataSource == NULL) {
75358 return MA_OUT_OF_MEMORY;
75359 }
75360
75361 /* Removed in 0.12. Set pDoneFence on the notifications. */
75362 notifications = pConfig->initNotifications;
75363 if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) {
75364 notifications.done.pFence = pConfig->pDoneFence;
75365 }
75366
75367 /*
75368 We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does
75369 not return prematurely before the sound has finished initializing.
75370 */
75371 if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }
75372 {
75373 ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init();
75374 resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath;
75375 resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW;
75376 resourceManagerDataSourceConfig.flags = flags;
75377 resourceManagerDataSourceConfig.pNotifications = &notifications;
75378 resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames;
75379 resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
75380 resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
75381 resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
75382 resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
75383 resourceManagerDataSourceConfig.isLooping = pConfig->isLooping;
75384
75385 result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource);
75386 if (result != MA_SUCCESS) {
75387 goto done;
75388 }
75389
75390 pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */
75391
75392 /* We need to use a slightly customized version of the config so we'll need to make a copy. */
75393 config = *pConfig;
75394 config.pFilePath = NULL;
75395 config.pFilePathW = NULL;
75396 config.pDataSource = pSound->pResourceManagerDataSource;
75397
75398 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
75399 if (result != MA_SUCCESS) {
75400 ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
75401 ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
75402 MA_ZERO_OBJECT(pSound);
75403 goto done;
75404 }
75405 }
75406 done:
75407 if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }
75408 return result;
75409 }
75410
75411 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)
75412 {
75413 ma_sound_config config;
75414
75415 if (pFilePath == NULL) {
75416 return MA_INVALID_ARGS;
75417 }
75418
75419 config = ma_sound_config_init_2(pEngine);
75420 config.pFilePath = pFilePath;
75421 config.flags = flags;
75422 config.pInitialAttachment = pGroup;
75423 config.pDoneFence = pDoneFence;
75424
75425 return ma_sound_init_ex(pEngine, &config, pSound);
75426 }
75427
75428 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)
75429 {
75430 ma_sound_config config;
75431
75432 if (pFilePath == NULL) {
75433 return MA_INVALID_ARGS;
75434 }
75435
75436 config = ma_sound_config_init_2(pEngine);
75437 config.pFilePathW = pFilePath;
75438 config.flags = flags;
75439 config.pInitialAttachment = pGroup;
75440 config.pDoneFence = pDoneFence;
75441
75442 return ma_sound_init_ex(pEngine, &config, pSound);
75443 }
75444
75445 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)
75446 {
75447 ma_result result;
75448 ma_sound_config config;
75449
75450 result = ma_sound_preinit(pEngine, pSound);
75451 if (result != MA_SUCCESS) {
75452 return result;
75453 }
75454
75455 if (pExistingSound == NULL) {
75456 return MA_INVALID_ARGS;
75457 }
75458
75459 /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */
75460 if (pExistingSound->pResourceManagerDataSource == NULL) {
75461 return MA_INVALID_OPERATION;
75462 }
75463
75464 /*
75465 We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream)
75466 this will fail.
75467 */
75468 pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);
75469 if (pSound->pResourceManagerDataSource == NULL) {
75470 return MA_OUT_OF_MEMORY;
75471 }
75472
75473 result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource);
75474 if (result != MA_SUCCESS) {
75475 ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
75476 return result;
75477 }
75478
75479 config = ma_sound_config_init_2(pEngine);
75480 config.pDataSource = pSound->pResourceManagerDataSource;
75481 config.flags = flags;
75482 config.pInitialAttachment = pGroup;
75483 config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode;
75484 config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames;
75485
75486 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
75487 if (result != MA_SUCCESS) {
75488 ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
75489 ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
75490 MA_ZERO_OBJECT(pSound);
75491 return result;
75492 }
75493
75494 /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */
75495 pSound->ownsDataSource = MA_TRUE;
75496
75497 return MA_SUCCESS;
75498 }
75499 #endif
75500
75501 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)
75502 {
75503 ma_sound_config config = ma_sound_config_init_2(pEngine);
75504 config.pDataSource = pDataSource;
75505 config.flags = flags;
75506 config.pInitialAttachment = pGroup;
75507 return ma_sound_init_ex(pEngine, &config, pSound);
75508 }
75509
75510 MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
75511 {
75512 ma_result result;
75513
75514 result = ma_sound_preinit(pEngine, pSound);
75515 if (result != MA_SUCCESS) {
75516 return result;
75517 }
75518
75519 if (pConfig == NULL) {
75520 return MA_INVALID_ARGS;
75521 }
75522
75523 pSound->endCallback = pConfig->endCallback;
75524 pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData;
75525
75526 /* We need to load the sound differently depending on whether or not we're loading from a file. */
75527 #ifndef MA_NO_RESOURCE_MANAGER
75528 if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {
75529 return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);
75530 } else
75531 #endif
75532 {
75533 /*
75534 Getting here means we're not loading from a file. We may be loading from an already-initialized
75535 data source, or none at all. If we aren't specifying any data source, we'll be initializing the
75536 the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this
75537 for us, so no special treatment required here.
75538 */
75539 return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);
75540 }
75541 }
75542
75543 MA_API void ma_sound_uninit(ma_sound* pSound)
75544 {
75545 if (pSound == NULL) {
75546 return;
75547 }
75548
75549 /*
75550 Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done
75551 so which makes thread safety beyond this point trivial.
75552 */
75553 ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks);
75554
75555 /* 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. */
75556 #ifndef MA_NO_RESOURCE_MANAGER
75557 if (pSound->ownsDataSource) {
75558 ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
75559 ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks);
75560 pSound->pDataSource = NULL;
75561 }
75562 #else
75563 MA_ASSERT(pSound->ownsDataSource == MA_FALSE);
75564 #endif
75565 }
75566
75567 MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound)
75568 {
75569 if (pSound == NULL) {
75570 return NULL;
75571 }
75572
75573 return pSound->engineNode.pEngine;
75574 }
75575
75576 MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound)
75577 {
75578 if (pSound == NULL) {
75579 return NULL;
75580 }
75581
75582 return pSound->pDataSource;
75583 }
75584
75585 MA_API ma_result ma_sound_start(ma_sound* pSound)
75586 {
75587 if (pSound == NULL) {
75588 return MA_INVALID_ARGS;
75589 }
75590
75591 /* If the sound is already playing, do nothing. */
75592 if (ma_sound_is_playing(pSound)) {
75593 return MA_SUCCESS;
75594 }
75595
75596 /* If the sound is at the end it means we want to start from the start again. */
75597 if (ma_sound_at_end(pSound)) {
75598 ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0);
75599 if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {
75600 return result; /* Failed to seek back to the start. */
75601 }
75602
75603 /* Make sure we clear the end indicator. */
75604 c89atomic_exchange_32(&pSound->atEnd, MA_FALSE);
75605 }
75606
75607 /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */
75608 ma_node_set_state(pSound, ma_node_state_started);
75609
75610 return MA_SUCCESS;
75611 }
75612
75613 MA_API ma_result ma_sound_stop(ma_sound* pSound)
75614 {
75615 if (pSound == NULL) {
75616 return MA_INVALID_ARGS;
75617 }
75618
75619 /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */
75620 ma_node_set_state(pSound, ma_node_state_stopped);
75621
75622 return MA_SUCCESS;
75623 }
75624
75625 MA_API void ma_sound_set_volume(ma_sound* pSound, float volume)
75626 {
75627 if (pSound == NULL) {
75628 return;
75629 }
75630
75631 ma_engine_node_set_volume(&pSound->engineNode, volume);
75632 }
75633
75634 MA_API float ma_sound_get_volume(const ma_sound* pSound)
75635 {
75636 float volume = 0;
75637
75638 if (pSound == NULL) {
75639 return 0;
75640 }
75641
75642 ma_engine_node_get_volume(&pSound->engineNode, &volume);
75643
75644 return volume;
75645 }
75646
75647 MA_API void ma_sound_set_pan(ma_sound* pSound, float pan)
75648 {
75649 if (pSound == NULL) {
75650 return;
75651 }
75652
75653 ma_panner_set_pan(&pSound->engineNode.panner, pan);
75654 }
75655
75656 MA_API float ma_sound_get_pan(const ma_sound* pSound)
75657 {
75658 if (pSound == NULL) {
75659 return 0;
75660 }
75661
75662 return ma_panner_get_pan(&pSound->engineNode.panner);
75663 }
75664
75665 MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode)
75666 {
75667 if (pSound == NULL) {
75668 return;
75669 }
75670
75671 ma_panner_set_mode(&pSound->engineNode.panner, panMode);
75672 }
75673
75674 MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound)
75675 {
75676 if (pSound == NULL) {
75677 return ma_pan_mode_balance;
75678 }
75679
75680 return ma_panner_get_mode(&pSound->engineNode.panner);
75681 }
75682
75683 MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch)
75684 {
75685 if (pSound == NULL) {
75686 return;
75687 }
75688
75689 if (pitch <= 0) {
75690 return;
75691 }
75692
75693 c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release);
75694 }
75695
75696 MA_API float ma_sound_get_pitch(const ma_sound* pSound)
75697 {
75698 if (pSound == NULL) {
75699 return 0;
75700 }
75701
75702 return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */
75703 }
75704
75705 MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled)
75706 {
75707 if (pSound == NULL) {
75708 return;
75709 }
75710
75711 c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release);
75712 }
75713
75714 MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound)
75715 {
75716 if (pSound == NULL) {
75717 return MA_FALSE;
75718 }
75719
75720 return ma_engine_node_is_spatialization_enabled(&pSound->engineNode);
75721 }
75722
75723 MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex)
75724 {
75725 if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) {
75726 return;
75727 }
75728
75729 c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release);
75730 }
75731
75732 MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound)
75733 {
75734 if (pSound == NULL) {
75735 return MA_LISTENER_INDEX_CLOSEST;
75736 }
75737
75738 return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire);
75739 }
75740
75741 MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound)
75742 {
75743 ma_uint32 listenerIndex;
75744
75745 if (pSound == NULL) {
75746 return 0;
75747 }
75748
75749 listenerIndex = ma_sound_get_pinned_listener_index(pSound);
75750 if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) {
75751 ma_vec3f position = ma_sound_get_position(pSound);
75752 return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z);
75753 }
75754
75755 return listenerIndex;
75756 }
75757
75758 MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound)
75759 {
75760 ma_vec3f relativePos;
75761 ma_engine* pEngine;
75762
75763 if (pSound == NULL) {
75764 return ma_vec3f_init_3f(0, 0, -1);
75765 }
75766
75767 pEngine = ma_sound_get_engine(pSound);
75768 if (pEngine == NULL) {
75769 return ma_vec3f_init_3f(0, 0, -1);
75770 }
75771
75772 ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL);
75773
75774 return ma_vec3f_normalize(ma_vec3f_neg(relativePos));
75775 }
75776
75777 MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z)
75778 {
75779 if (pSound == NULL) {
75780 return;
75781 }
75782
75783 ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z);
75784 }
75785
75786 MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound)
75787 {
75788 if (pSound == NULL) {
75789 return ma_vec3f_init_3f(0, 0, 0);
75790 }
75791
75792 return ma_spatializer_get_position(&pSound->engineNode.spatializer);
75793 }
75794
75795 MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z)
75796 {
75797 if (pSound == NULL) {
75798 return;
75799 }
75800
75801 ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z);
75802 }
75803
75804 MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound)
75805 {
75806 if (pSound == NULL) {
75807 return ma_vec3f_init_3f(0, 0, 0);
75808 }
75809
75810 return ma_spatializer_get_direction(&pSound->engineNode.spatializer);
75811 }
75812
75813 MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z)
75814 {
75815 if (pSound == NULL) {
75816 return;
75817 }
75818
75819 ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z);
75820 }
75821
75822 MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound)
75823 {
75824 if (pSound == NULL) {
75825 return ma_vec3f_init_3f(0, 0, 0);
75826 }
75827
75828 return ma_spatializer_get_velocity(&pSound->engineNode.spatializer);
75829 }
75830
75831 MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel)
75832 {
75833 if (pSound == NULL) {
75834 return;
75835 }
75836
75837 ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel);
75838 }
75839
75840 MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound)
75841 {
75842 if (pSound == NULL) {
75843 return ma_attenuation_model_none;
75844 }
75845
75846 return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer);
75847 }
75848
75849 MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning)
75850 {
75851 if (pSound == NULL) {
75852 return;
75853 }
75854
75855 ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning);
75856 }
75857
75858 MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound)
75859 {
75860 if (pSound == NULL) {
75861 return ma_positioning_absolute;
75862 }
75863
75864 return ma_spatializer_get_positioning(&pSound->engineNode.spatializer);
75865 }
75866
75867 MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff)
75868 {
75869 if (pSound == NULL) {
75870 return;
75871 }
75872
75873 ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff);
75874 }
75875
75876 MA_API float ma_sound_get_rolloff(const ma_sound* pSound)
75877 {
75878 if (pSound == NULL) {
75879 return 0;
75880 }
75881
75882 return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer);
75883 }
75884
75885 MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain)
75886 {
75887 if (pSound == NULL) {
75888 return;
75889 }
75890
75891 ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain);
75892 }
75893
75894 MA_API float ma_sound_get_min_gain(const ma_sound* pSound)
75895 {
75896 if (pSound == NULL) {
75897 return 0;
75898 }
75899
75900 return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer);
75901 }
75902
75903 MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain)
75904 {
75905 if (pSound == NULL) {
75906 return;
75907 }
75908
75909 ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain);
75910 }
75911
75912 MA_API float ma_sound_get_max_gain(const ma_sound* pSound)
75913 {
75914 if (pSound == NULL) {
75915 return 0;
75916 }
75917
75918 return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer);
75919 }
75920
75921 MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance)
75922 {
75923 if (pSound == NULL) {
75924 return;
75925 }
75926
75927 ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance);
75928 }
75929
75930 MA_API float ma_sound_get_min_distance(const ma_sound* pSound)
75931 {
75932 if (pSound == NULL) {
75933 return 0;
75934 }
75935
75936 return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer);
75937 }
75938
75939 MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance)
75940 {
75941 if (pSound == NULL) {
75942 return;
75943 }
75944
75945 ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance);
75946 }
75947
75948 MA_API float ma_sound_get_max_distance(const ma_sound* pSound)
75949 {
75950 if (pSound == NULL) {
75951 return 0;
75952 }
75953
75954 return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer);
75955 }
75956
75957 MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
75958 {
75959 if (pSound == NULL) {
75960 return;
75961 }
75962
75963 ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain);
75964 }
75965
75966 MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
75967 {
75968 if (pInnerAngleInRadians != NULL) {
75969 *pInnerAngleInRadians = 0;
75970 }
75971
75972 if (pOuterAngleInRadians != NULL) {
75973 *pOuterAngleInRadians = 0;
75974 }
75975
75976 if (pOuterGain != NULL) {
75977 *pOuterGain = 0;
75978 }
75979
75980 ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
75981 }
75982
75983 MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor)
75984 {
75985 if (pSound == NULL) {
75986 return;
75987 }
75988
75989 ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor);
75990 }
75991
75992 MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound)
75993 {
75994 if (pSound == NULL) {
75995 return 0;
75996 }
75997
75998 return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer);
75999 }
76000
76001 MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor)
76002 {
76003 if (pSound == NULL) {
76004 return;
76005 }
76006
76007 ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor);
76008 }
76009
76010 MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound)
76011 {
76012 if (pSound == NULL) {
76013 return 1;
76014 }
76015
76016 return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer);
76017 }
76018
76019
76020 MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
76021 {
76022 if (pSound == NULL) {
76023 return;
76024 }
76025
76026 ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames);
76027 }
76028
76029 MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
76030 {
76031 if (pSound == NULL) {
76032 return;
76033 }
76034
76035 ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000);
76036 }
76037
76038 MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound)
76039 {
76040 if (pSound == NULL) {
76041 return MA_INVALID_ARGS;
76042 }
76043
76044 return ma_fader_get_current_volume(&pSound->engineNode.fader);
76045 }
76046
76047 MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
76048 {
76049 if (pSound == NULL) {
76050 return;
76051 }
76052
76053 ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames);
76054 }
76055
76056 MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
76057 {
76058 if (pSound == NULL) {
76059 return;
76060 }
76061
76062 ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
76063 }
76064
76065 MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
76066 {
76067 if (pSound == NULL) {
76068 return;
76069 }
76070
76071 ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames);
76072 }
76073
76074 MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
76075 {
76076 if (pSound == NULL) {
76077 return;
76078 }
76079
76080 ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
76081 }
76082
76083 MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound)
76084 {
76085 if (pSound == NULL) {
76086 return MA_FALSE;
76087 }
76088
76089 return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started;
76090 }
76091
76092 MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound)
76093 {
76094 if (pSound == NULL) {
76095 return 0;
76096 }
76097
76098 return ma_node_get_time(pSound);
76099 }
76100
76101 MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)
76102 {
76103 if (pSound == NULL) {
76104 return;
76105 }
76106
76107 /* Looping is only a valid concept if the sound is backed by a data source. */
76108 if (pSound->pDataSource == NULL) {
76109 return;
76110 }
76111
76112 /* The looping state needs to be applied to the data source in order for any looping to actually happen. */
76113 ma_data_source_set_looping(pSound->pDataSource, isLooping);
76114 }
76115
76116 MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound)
76117 {
76118 if (pSound == NULL) {
76119 return MA_FALSE;
76120 }
76121
76122 /* There is no notion of looping for sounds that are not backed by a data source. */
76123 if (pSound->pDataSource == NULL) {
76124 return MA_FALSE;
76125 }
76126
76127 return ma_data_source_is_looping(pSound->pDataSource);
76128 }
76129
76130 MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound)
76131 {
76132 if (pSound == NULL) {
76133 return MA_FALSE;
76134 }
76135
76136 /* There is no notion of an end of a sound if it's not backed by a data source. */
76137 if (pSound->pDataSource == NULL) {
76138 return MA_FALSE;
76139 }
76140
76141 return ma_sound_get_at_end(pSound);
76142 }
76143
76144 MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex)
76145 {
76146 if (pSound == NULL) {
76147 return MA_INVALID_ARGS;
76148 }
76149
76150 /* Seeking is only valid for sounds that are backed by a data source. */
76151 if (pSound->pDataSource == NULL) {
76152 return MA_INVALID_OPERATION;
76153 }
76154
76155 /* 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. */
76156 c89atomic_exchange_64(&pSound->seekTarget, frameIndex);
76157
76158 return MA_SUCCESS;
76159 }
76160
76161 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)
76162 {
76163 if (pSound == NULL) {
76164 return MA_INVALID_ARGS;
76165 }
76166
76167 /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */
76168 if (pSound->pDataSource == NULL) {
76169 ma_uint32 channels;
76170
76171 if (pFormat != NULL) {
76172 *pFormat = ma_format_f32;
76173 }
76174
76175 channels = ma_node_get_input_channels(&pSound->engineNode, 0);
76176 if (pChannels != NULL) {
76177 *pChannels = channels;
76178 }
76179
76180 if (pSampleRate != NULL) {
76181 *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn;
76182 }
76183
76184 if (pChannelMap != NULL) {
76185 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels);
76186 }
76187
76188 return MA_SUCCESS;
76189 } else {
76190 return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
76191 }
76192 }
76193
76194 MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor)
76195 {
76196 if (pSound == NULL) {
76197 return MA_INVALID_ARGS;
76198 }
76199
76200 /* The notion of a cursor is only valid for sounds that are backed by a data source. */
76201 if (pSound->pDataSource == NULL) {
76202 return MA_INVALID_OPERATION;
76203 }
76204
76205 return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor);
76206 }
76207
76208 MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength)
76209 {
76210 if (pSound == NULL) {
76211 return MA_INVALID_ARGS;
76212 }
76213
76214 /* The notion of a sound length is only valid for sounds that are backed by a data source. */
76215 if (pSound->pDataSource == NULL) {
76216 return MA_INVALID_OPERATION;
76217 }
76218
76219 return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength);
76220 }
76221
76222 MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor)
76223 {
76224 if (pSound == NULL) {
76225 return MA_INVALID_ARGS;
76226 }
76227
76228 /* The notion of a cursor is only valid for sounds that are backed by a data source. */
76229 if (pSound->pDataSource == NULL) {
76230 return MA_INVALID_OPERATION;
76231 }
76232
76233 return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor);
76234 }
76235
76236 MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength)
76237 {
76238 if (pSound == NULL) {
76239 return MA_INVALID_ARGS;
76240 }
76241
76242 /* The notion of a sound length is only valid for sounds that are backed by a data source. */
76243 if (pSound->pDataSource == NULL) {
76244 return MA_INVALID_OPERATION;
76245 }
76246
76247 return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength);
76248 }
76249
76250 MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData)
76251 {
76252 if (pSound == NULL) {
76253 return MA_INVALID_ARGS;
76254 }
76255
76256 /* The notion of an end is only valid for sounds that are backed by a data source. */
76257 if (pSound->pDataSource == NULL) {
76258 return MA_INVALID_OPERATION;
76259 }
76260
76261 pSound->endCallback = callback;
76262 pSound->pEndCallbackUserData = pUserData;
76263
76264 return MA_SUCCESS;
76265 }
76266
76267
76268 MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup)
76269 {
76270 ma_sound_group_config config = ma_sound_group_config_init_2(pEngine);
76271 config.flags = flags;
76272 config.pInitialAttachment = pParentGroup;
76273 return ma_sound_group_init_ex(pEngine, &config, pGroup);
76274 }
76275
76276 MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup)
76277 {
76278 ma_sound_config soundConfig;
76279
76280 if (pGroup == NULL) {
76281 return MA_INVALID_ARGS;
76282 }
76283
76284 MA_ZERO_OBJECT(pGroup);
76285
76286 if (pConfig == NULL) {
76287 return MA_INVALID_ARGS;
76288 }
76289
76290 /* A sound group is just a sound without a data source. */
76291 soundConfig = *pConfig;
76292 soundConfig.pFilePath = NULL;
76293 soundConfig.pFilePathW = NULL;
76294 soundConfig.pDataSource = NULL;
76295
76296 /*
76297 Groups need to have spatialization disabled by default because I think it'll be pretty rare
76298 that programs will want to spatialize groups (but not unheard of). Certainly it feels like
76299 disabling this by default feels like the right option. Spatialization can be enabled with a
76300 call to ma_sound_group_set_spatialization_enabled().
76301 */
76302 soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION;
76303
76304 return ma_sound_init_ex(pEngine, &soundConfig, pGroup);
76305 }
76306
76307 MA_API void ma_sound_group_uninit(ma_sound_group* pGroup)
76308 {
76309 ma_sound_uninit(pGroup);
76310 }
76311
76312 MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup)
76313 {
76314 return ma_sound_get_engine(pGroup);
76315 }
76316
76317 MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup)
76318 {
76319 return ma_sound_start(pGroup);
76320 }
76321
76322 MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup)
76323 {
76324 return ma_sound_stop(pGroup);
76325 }
76326
76327 MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume)
76328 {
76329 ma_sound_set_volume(pGroup, volume);
76330 }
76331
76332 MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup)
76333 {
76334 return ma_sound_get_volume(pGroup);
76335 }
76336
76337 MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan)
76338 {
76339 ma_sound_set_pan(pGroup, pan);
76340 }
76341
76342 MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup)
76343 {
76344 return ma_sound_get_pan(pGroup);
76345 }
76346
76347 MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode)
76348 {
76349 ma_sound_set_pan_mode(pGroup, panMode);
76350 }
76351
76352 MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup)
76353 {
76354 return ma_sound_get_pan_mode(pGroup);
76355 }
76356
76357 MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch)
76358 {
76359 ma_sound_set_pitch(pGroup, pitch);
76360 }
76361
76362 MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup)
76363 {
76364 return ma_sound_get_pitch(pGroup);
76365 }
76366
76367 MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled)
76368 {
76369 ma_sound_set_spatialization_enabled(pGroup, enabled);
76370 }
76371
76372 MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup)
76373 {
76374 return ma_sound_is_spatialization_enabled(pGroup);
76375 }
76376
76377 MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex)
76378 {
76379 ma_sound_set_pinned_listener_index(pGroup, listenerIndex);
76380 }
76381
76382 MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup)
76383 {
76384 return ma_sound_get_pinned_listener_index(pGroup);
76385 }
76386
76387 MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup)
76388 {
76389 return ma_sound_get_listener_index(pGroup);
76390 }
76391
76392 MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup)
76393 {
76394 return ma_sound_get_direction_to_listener(pGroup);
76395 }
76396
76397 MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z)
76398 {
76399 ma_sound_set_position(pGroup, x, y, z);
76400 }
76401
76402 MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup)
76403 {
76404 return ma_sound_get_position(pGroup);
76405 }
76406
76407 MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z)
76408 {
76409 ma_sound_set_direction(pGroup, x, y, z);
76410 }
76411
76412 MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup)
76413 {
76414 return ma_sound_get_direction(pGroup);
76415 }
76416
76417 MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z)
76418 {
76419 ma_sound_set_velocity(pGroup, x, y, z);
76420 }
76421
76422 MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup)
76423 {
76424 return ma_sound_get_velocity(pGroup);
76425 }
76426
76427 MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel)
76428 {
76429 ma_sound_set_attenuation_model(pGroup, attenuationModel);
76430 }
76431
76432 MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup)
76433 {
76434 return ma_sound_get_attenuation_model(pGroup);
76435 }
76436
76437 MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning)
76438 {
76439 ma_sound_set_positioning(pGroup, positioning);
76440 }
76441
76442 MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup)
76443 {
76444 return ma_sound_get_positioning(pGroup);
76445 }
76446
76447 MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff)
76448 {
76449 ma_sound_set_rolloff(pGroup, rolloff);
76450 }
76451
76452 MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup)
76453 {
76454 return ma_sound_get_rolloff(pGroup);
76455 }
76456
76457 MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain)
76458 {
76459 ma_sound_set_min_gain(pGroup, minGain);
76460 }
76461
76462 MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup)
76463 {
76464 return ma_sound_get_min_gain(pGroup);
76465 }
76466
76467 MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain)
76468 {
76469 ma_sound_set_max_gain(pGroup, maxGain);
76470 }
76471
76472 MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup)
76473 {
76474 return ma_sound_get_max_gain(pGroup);
76475 }
76476
76477 MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance)
76478 {
76479 ma_sound_set_min_distance(pGroup, minDistance);
76480 }
76481
76482 MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup)
76483 {
76484 return ma_sound_get_min_distance(pGroup);
76485 }
76486
76487 MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance)
76488 {
76489 ma_sound_set_max_distance(pGroup, maxDistance);
76490 }
76491
76492 MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup)
76493 {
76494 return ma_sound_get_max_distance(pGroup);
76495 }
76496
76497 MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
76498 {
76499 ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain);
76500 }
76501
76502 MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
76503 {
76504 ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
76505 }
76506
76507 MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor)
76508 {
76509 ma_sound_set_doppler_factor(pGroup, dopplerFactor);
76510 }
76511
76512 MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup)
76513 {
76514 return ma_sound_get_doppler_factor(pGroup);
76515 }
76516
76517 MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor)
76518 {
76519 ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor);
76520 }
76521
76522 MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup)
76523 {
76524 return ma_sound_get_directional_attenuation_factor(pGroup);
76525 }
76526
76527 MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
76528 {
76529 ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames);
76530 }
76531
76532 MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
76533 {
76534 ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds);
76535 }
76536
76537 MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup)
76538 {
76539 return ma_sound_get_current_fade_volume(pGroup);
76540 }
76541
76542 MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
76543 {
76544 ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
76545 }
76546
76547 MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
76548 {
76549 ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
76550 }
76551
76552 MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
76553 {
76554 ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
76555 }
76556
76557 MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
76558 {
76559 ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
76560 }
76561
76562 MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup)
76563 {
76564 return ma_sound_is_playing(pGroup);
76565 }
76566
76567 MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup)
76568 {
76569 return ma_sound_get_time_in_pcm_frames(pGroup);
76570 }
76571 #endif /* MA_NO_ENGINE */
76572 /* END SECTION: miniaudio_engine.c */
76573
76574
76575
76576 /**************************************************************************************************************************************************************
76577 ***************************************************************************************************************************************************************
76578
76579 Auto Generated
76580 ==============
76581 All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the
76582 code below please report the bug to the respective repository for the relevant project (probably dr_libs).
76583
76584 ***************************************************************************************************************************************************************
76585 **************************************************************************************************************************************************************/
76586 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
76587 #if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
76588 /* dr_wav_c begin */
76589 #ifndef dr_wav_c
76590 #define dr_wav_c
76591 #ifdef __MRC__
76592 #pragma options opt off
76593 #endif
76594 #include <stdlib.h>
76595 #include <string.h>
76596 #include <limits.h>
76597 #ifndef DR_WAV_NO_STDIO
76598 #include <stdio.h>
76599 #ifndef DR_WAV_NO_WCHAR
76600 #include <wchar.h>
76601 #endif
76602 #endif
76603 #ifndef DRWAV_ASSERT
76604 #include <assert.h>
76605 #define DRWAV_ASSERT(expression) assert(expression)
76606 #endif
76607 #ifndef DRWAV_MALLOC
76608 #define DRWAV_MALLOC(sz) malloc((sz))
76609 #endif
76610 #ifndef DRWAV_REALLOC
76611 #define DRWAV_REALLOC(p, sz) realloc((p), (sz))
76612 #endif
76613 #ifndef DRWAV_FREE
76614 #define DRWAV_FREE(p) free((p))
76615 #endif
76616 #ifndef DRWAV_COPY_MEMORY
76617 #define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
76618 #endif
76619 #ifndef DRWAV_ZERO_MEMORY
76620 #define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
76621 #endif
76622 #ifndef DRWAV_ZERO_OBJECT
76623 #define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p))
76624 #endif
76625 #define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
76626 #define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
76627 #define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
76628 #define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
76629 #define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
76630 #define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset))
76631 #define DRWAV_MAX_SIMD_VECTOR_SIZE 64
76632 #if defined(__x86_64__) || defined(_M_X64)
76633 #define DRWAV_X64
76634 #elif defined(__i386) || defined(_M_IX86)
76635 #define DRWAV_X86
76636 #elif defined(__arm__) || defined(_M_ARM)
76637 #define DRWAV_ARM
76638 #endif
76639 #ifdef _MSC_VER
76640 #define DRWAV_INLINE __forceinline
76641 #elif defined(__GNUC__)
76642 #if defined(__STRICT_ANSI__)
76643 #define DRWAV_GNUC_INLINE_HINT __inline__
76644 #else
76645 #define DRWAV_GNUC_INLINE_HINT inline
76646 #endif
76647 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
76648 #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline))
76649 #else
76650 #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT
76651 #endif
76652 #elif defined(__WATCOMC__)
76653 #define DRWAV_INLINE __inline
76654 #else
76655 #define DRWAV_INLINE
76656 #endif
76657 #if defined(SIZE_MAX)
76658 #define DRWAV_SIZE_MAX SIZE_MAX
76659 #else
76660 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
76661 #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
76662 #else
76663 #define DRWAV_SIZE_MAX 0xFFFFFFFF
76664 #endif
76665 #endif
76666 #if defined(_MSC_VER) && _MSC_VER >= 1400
76667 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
76668 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
76669 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
76670 #elif defined(__clang__)
76671 #if defined(__has_builtin)
76672 #if __has_builtin(__builtin_bswap16)
76673 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
76674 #endif
76675 #if __has_builtin(__builtin_bswap32)
76676 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
76677 #endif
76678 #if __has_builtin(__builtin_bswap64)
76679 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
76680 #endif
76681 #endif
76682 #elif defined(__GNUC__)
76683 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
76684 #define DRWAV_HAS_BYTESWAP32_INTRINSIC
76685 #define DRWAV_HAS_BYTESWAP64_INTRINSIC
76686 #endif
76687 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
76688 #define DRWAV_HAS_BYTESWAP16_INTRINSIC
76689 #endif
76690 #endif
76691 DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision)
76692 {
76693 if (pMajor) {
76694 *pMajor = DRWAV_VERSION_MAJOR;
76695 }
76696 if (pMinor) {
76697 *pMinor = DRWAV_VERSION_MINOR;
76698 }
76699 if (pRevision) {
76700 *pRevision = DRWAV_VERSION_REVISION;
76701 }
76702 }
76703 DRWAV_API const char* drwav_version_string(void)
76704 {
76705 return DRWAV_VERSION_STRING;
76706 }
76707 #ifndef DRWAV_MAX_SAMPLE_RATE
76708 #define DRWAV_MAX_SAMPLE_RATE 384000
76709 #endif
76710 #ifndef DRWAV_MAX_CHANNELS
76711 #define DRWAV_MAX_CHANNELS 256
76712 #endif
76713 #ifndef DRWAV_MAX_BITS_PER_SAMPLE
76714 #define DRWAV_MAX_BITS_PER_SAMPLE 64
76715 #endif
76716 static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};
76717 static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
76718 static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
76719 static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
76720 static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
76721 static DRWAV_INLINE int drwav__is_little_endian(void)
76722 {
76723 #if defined(DRWAV_X86) || defined(DRWAV_X64)
76724 return DRWAV_TRUE;
76725 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
76726 return DRWAV_TRUE;
76727 #else
76728 int n = 1;
76729 return (*(char*)&n) == 1;
76730 #endif
76731 }
76732 static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid)
76733 {
76734 int i;
76735 for (i = 0; i < 16; ++i) {
76736 guid[i] = data[i];
76737 }
76738 }
76739 static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)
76740 {
76741 #ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC
76742 #if defined(_MSC_VER)
76743 return _byteswap_ushort(n);
76744 #elif defined(__GNUC__) || defined(__clang__)
76745 return __builtin_bswap16(n);
76746 #else
76747 #error "This compiler does not support the byte swap intrinsic."
76748 #endif
76749 #else
76750 return ((n & 0xFF00) >> 8) |
76751 ((n & 0x00FF) << 8);
76752 #endif
76753 }
76754 static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)
76755 {
76756 #ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
76757 #if defined(_MSC_VER)
76758 return _byteswap_ulong(n);
76759 #elif defined(__GNUC__) || defined(__clang__)
76760 #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT)
76761 drwav_uint32 r;
76762 __asm__ __volatile__ (
76763 #if defined(DRWAV_64BIT)
76764 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
76765 #else
76766 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
76767 #endif
76768 );
76769 return r;
76770 #else
76771 return __builtin_bswap32(n);
76772 #endif
76773 #else
76774 #error "This compiler does not support the byte swap intrinsic."
76775 #endif
76776 #else
76777 return ((n & 0xFF000000) >> 24) |
76778 ((n & 0x00FF0000) >> 8) |
76779 ((n & 0x0000FF00) << 8) |
76780 ((n & 0x000000FF) << 24);
76781 #endif
76782 }
76783 static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)
76784 {
76785 #ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC
76786 #if defined(_MSC_VER)
76787 return _byteswap_uint64(n);
76788 #elif defined(__GNUC__) || defined(__clang__)
76789 return __builtin_bswap64(n);
76790 #else
76791 #error "This compiler does not support the byte swap intrinsic."
76792 #endif
76793 #else
76794 return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) |
76795 ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) |
76796 ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) |
76797 ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) |
76798 ((n & ((drwav_uint64)0xFF000000 )) << 8) |
76799 ((n & ((drwav_uint64)0x00FF0000 )) << 24) |
76800 ((n & ((drwav_uint64)0x0000FF00 )) << 40) |
76801 ((n & ((drwav_uint64)0x000000FF )) << 56);
76802 #endif
76803 }
76804 static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)
76805 {
76806 return (drwav_int16)drwav__bswap16((drwav_uint16)n);
76807 }
76808 static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)
76809 {
76810 drwav_uint64 iSample;
76811 for (iSample = 0; iSample < sampleCount; iSample += 1) {
76812 pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);
76813 }
76814 }
76815 static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)
76816 {
76817 drwav_uint8 t;
76818 t = p[0];
76819 p[0] = p[2];
76820 p[2] = t;
76821 }
76822 static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)
76823 {
76824 drwav_uint64 iSample;
76825 for (iSample = 0; iSample < sampleCount; iSample += 1) {
76826 drwav_uint8* pSample = pSamples + (iSample*3);
76827 drwav__bswap_s24(pSample);
76828 }
76829 }
76830 static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)
76831 {
76832 return (drwav_int32)drwav__bswap32((drwav_uint32)n);
76833 }
76834 static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)
76835 {
76836 drwav_uint64 iSample;
76837 for (iSample = 0; iSample < sampleCount; iSample += 1) {
76838 pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);
76839 }
76840 }
76841 static DRWAV_INLINE float drwav__bswap_f32(float n)
76842 {
76843 union {
76844 drwav_uint32 i;
76845 float f;
76846 } x;
76847 x.f = n;
76848 x.i = drwav__bswap32(x.i);
76849 return x.f;
76850 }
76851 static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)
76852 {
76853 drwav_uint64 iSample;
76854 for (iSample = 0; iSample < sampleCount; iSample += 1) {
76855 pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);
76856 }
76857 }
76858 static DRWAV_INLINE double drwav__bswap_f64(double n)
76859 {
76860 union {
76861 drwav_uint64 i;
76862 double f;
76863 } x;
76864 x.f = n;
76865 x.i = drwav__bswap64(x.i);
76866 return x.f;
76867 }
76868 static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount)
76869 {
76870 drwav_uint64 iSample;
76871 for (iSample = 0; iSample < sampleCount; iSample += 1) {
76872 pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]);
76873 }
76874 }
76875 static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
76876 {
76877 switch (bytesPerSample)
76878 {
76879 case 1:
76880 {
76881 } break;
76882 case 2:
76883 {
76884 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
76885 } break;
76886 case 3:
76887 {
76888 drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);
76889 } break;
76890 case 4:
76891 {
76892 drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);
76893 } break;
76894 default:
76895 {
76896 DRWAV_ASSERT(DRWAV_FALSE);
76897 } break;
76898 }
76899 }
76900 static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
76901 {
76902 switch (bytesPerSample)
76903 {
76904 #if 0
76905 case 2:
76906 {
76907 drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount);
76908 } break;
76909 #endif
76910 case 4:
76911 {
76912 drwav__bswap_samples_f32((float*)pSamples, sampleCount);
76913 } break;
76914 case 8:
76915 {
76916 drwav__bswap_samples_f64((double*)pSamples, sampleCount);
76917 } break;
76918 default:
76919 {
76920 DRWAV_ASSERT(DRWAV_FALSE);
76921 } break;
76922 }
76923 }
76924 static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format)
76925 {
76926 switch (format)
76927 {
76928 case DR_WAVE_FORMAT_PCM:
76929 {
76930 drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample);
76931 } break;
76932 case DR_WAVE_FORMAT_IEEE_FLOAT:
76933 {
76934 drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample);
76935 } break;
76936 case DR_WAVE_FORMAT_ALAW:
76937 case DR_WAVE_FORMAT_MULAW:
76938 {
76939 drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
76940 } break;
76941 case DR_WAVE_FORMAT_ADPCM:
76942 case DR_WAVE_FORMAT_DVI_ADPCM:
76943 default:
76944 {
76945 DRWAV_ASSERT(DRWAV_FALSE);
76946 } break;
76947 }
76948 }
76949 DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData)
76950 {
76951 (void)pUserData;
76952 return DRWAV_MALLOC(sz);
76953 }
76954 DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData)
76955 {
76956 (void)pUserData;
76957 return DRWAV_REALLOC(p, sz);
76958 }
76959 DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData)
76960 {
76961 (void)pUserData;
76962 DRWAV_FREE(p);
76963 }
76964 DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)
76965 {
76966 if (pAllocationCallbacks == NULL) {
76967 return NULL;
76968 }
76969 if (pAllocationCallbacks->onMalloc != NULL) {
76970 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
76971 }
76972 if (pAllocationCallbacks->onRealloc != NULL) {
76973 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
76974 }
76975 return NULL;
76976 }
76977 DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)
76978 {
76979 if (pAllocationCallbacks == NULL) {
76980 return NULL;
76981 }
76982 if (pAllocationCallbacks->onRealloc != NULL) {
76983 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
76984 }
76985 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
76986 void* p2;
76987 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
76988 if (p2 == NULL) {
76989 return NULL;
76990 }
76991 if (p != NULL) {
76992 DRWAV_COPY_MEMORY(p2, p, szOld);
76993 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
76994 }
76995 return p2;
76996 }
76997 return NULL;
76998 }
76999 DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
77000 {
77001 if (p == NULL || pAllocationCallbacks == NULL) {
77002 return;
77003 }
77004 if (pAllocationCallbacks->onFree != NULL) {
77005 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
77006 }
77007 }
77008 DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)
77009 {
77010 if (pAllocationCallbacks != NULL) {
77011 return *pAllocationCallbacks;
77012 } else {
77013 drwav_allocation_callbacks allocationCallbacks;
77014 allocationCallbacks.pUserData = NULL;
77015 allocationCallbacks.onMalloc = drwav__malloc_default;
77016 allocationCallbacks.onRealloc = drwav__realloc_default;
77017 allocationCallbacks.onFree = drwav__free_default;
77018 return allocationCallbacks;
77019 }
77020 }
77021 static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
77022 {
77023 return
77024 formatTag == DR_WAVE_FORMAT_ADPCM ||
77025 formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
77026 }
77027 DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)
77028 {
77029 return (unsigned int)(chunkSize % 2);
77030 }
77031 DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)
77032 {
77033 return (unsigned int)(chunkSize % 8);
77034 }
77035 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
77036 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
77037 DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
77038 DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
77039 {
77040 if (container == drwav_container_riff || container == drwav_container_rf64) {
77041 drwav_uint8 sizeInBytes[4];
77042 if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
77043 return DRWAV_AT_END;
77044 }
77045 if (onRead(pUserData, sizeInBytes, 4) != 4) {
77046 return DRWAV_INVALID_FILE;
77047 }
77048 pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes);
77049 pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
77050 *pRunningBytesReadOut += 8;
77051 } else {
77052 drwav_uint8 sizeInBytes[8];
77053 if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
77054 return DRWAV_AT_END;
77055 }
77056 if (onRead(pUserData, sizeInBytes, 8) != 8) {
77057 return DRWAV_INVALID_FILE;
77058 }
77059 pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24;
77060 pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
77061 *pRunningBytesReadOut += 24;
77062 }
77063 return DRWAV_SUCCESS;
77064 }
77065 DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
77066 {
77067 drwav_uint64 bytesRemainingToSeek = offset;
77068 while (bytesRemainingToSeek > 0) {
77069 if (bytesRemainingToSeek > 0x7FFFFFFF) {
77070 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
77071 return DRWAV_FALSE;
77072 }
77073 bytesRemainingToSeek -= 0x7FFFFFFF;
77074 } else {
77075 if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
77076 return DRWAV_FALSE;
77077 }
77078 bytesRemainingToSeek = 0;
77079 }
77080 }
77081 return DRWAV_TRUE;
77082 }
77083 DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
77084 {
77085 if (offset <= 0x7FFFFFFF) {
77086 return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
77087 }
77088 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
77089 return DRWAV_FALSE;
77090 }
77091 offset -= 0x7FFFFFFF;
77092 for (;;) {
77093 if (offset <= 0x7FFFFFFF) {
77094 return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
77095 }
77096 if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
77097 return DRWAV_FALSE;
77098 }
77099 offset -= 0x7FFFFFFF;
77100 }
77101 }
77102 DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
77103 {
77104 drwav_chunk_header header;
77105 drwav_uint8 fmt[16];
77106 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
77107 return DRWAV_FALSE;
77108 }
77109 while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
77110 if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
77111 return DRWAV_FALSE;
77112 }
77113 *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
77114 if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
77115 return DRWAV_FALSE;
77116 }
77117 }
77118 if (container == drwav_container_riff || container == drwav_container_rf64) {
77119 if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) {
77120 return DRWAV_FALSE;
77121 }
77122 } else {
77123 if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
77124 return DRWAV_FALSE;
77125 }
77126 }
77127 if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
77128 return DRWAV_FALSE;
77129 }
77130 *pRunningBytesReadOut += sizeof(fmt);
77131 fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0);
77132 fmtOut->channels = drwav_bytes_to_u16(fmt + 2);
77133 fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4);
77134 fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8);
77135 fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12);
77136 fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14);
77137 fmtOut->extendedSize = 0;
77138 fmtOut->validBitsPerSample = 0;
77139 fmtOut->channelMask = 0;
77140 DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat));
77141 if (header.sizeInBytes > 16) {
77142 drwav_uint8 fmt_cbSize[2];
77143 int bytesReadSoFar = 0;
77144 if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
77145 return DRWAV_FALSE;
77146 }
77147 *pRunningBytesReadOut += sizeof(fmt_cbSize);
77148 bytesReadSoFar = 18;
77149 fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize);
77150 if (fmtOut->extendedSize > 0) {
77151 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
77152 if (fmtOut->extendedSize != 22) {
77153 return DRWAV_FALSE;
77154 }
77155 }
77156 if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
77157 drwav_uint8 fmtext[22];
77158 if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
77159 return DRWAV_FALSE;
77160 }
77161 fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0);
77162 fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2);
77163 drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat);
77164 } else {
77165 if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
77166 return DRWAV_FALSE;
77167 }
77168 }
77169 *pRunningBytesReadOut += fmtOut->extendedSize;
77170 bytesReadSoFar += fmtOut->extendedSize;
77171 }
77172 if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
77173 return DRWAV_FALSE;
77174 }
77175 *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
77176 }
77177 if (header.paddingSize > 0) {
77178 if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
77179 return DRWAV_FALSE;
77180 }
77181 *pRunningBytesReadOut += header.paddingSize;
77182 }
77183 return DRWAV_TRUE;
77184 }
77185 DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
77186 {
77187 size_t bytesRead;
77188 DRWAV_ASSERT(onRead != NULL);
77189 DRWAV_ASSERT(pCursor != NULL);
77190 bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
77191 *pCursor += bytesRead;
77192 return bytesRead;
77193 }
77194 #if 0
77195 DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
77196 {
77197 DRWAV_ASSERT(onSeek != NULL);
77198 DRWAV_ASSERT(pCursor != NULL);
77199 if (!onSeek(pUserData, offset, origin)) {
77200 return DRWAV_FALSE;
77201 }
77202 if (origin == drwav_seek_origin_start) {
77203 *pCursor = offset;
77204 } else {
77205 *pCursor += offset;
77206 }
77207 return DRWAV_TRUE;
77208 }
77209 #endif
77210 #define DRWAV_SMPL_BYTES 36
77211 #define DRWAV_SMPL_LOOP_BYTES 24
77212 #define DRWAV_INST_BYTES 7
77213 #define DRWAV_ACID_BYTES 24
77214 #define DRWAV_CUE_BYTES 4
77215 #define DRWAV_BEXT_BYTES 602
77216 #define DRWAV_BEXT_DESCRIPTION_BYTES 256
77217 #define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32
77218 #define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32
77219 #define DRWAV_BEXT_RESERVED_BYTES 180
77220 #define DRWAV_BEXT_UMID_BYTES 64
77221 #define DRWAV_CUE_POINT_BYTES 24
77222 #define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4
77223 #define DRWAV_LIST_LABELLED_TEXT_BYTES 20
77224 #define DRWAV_METADATA_ALIGNMENT 8
77225 typedef enum
77226 {
77227 drwav__metadata_parser_stage_count,
77228 drwav__metadata_parser_stage_read
77229 } drwav__metadata_parser_stage;
77230 typedef struct
77231 {
77232 drwav_read_proc onRead;
77233 drwav_seek_proc onSeek;
77234 void *pReadSeekUserData;
77235 drwav__metadata_parser_stage stage;
77236 drwav_metadata *pMetadata;
77237 drwav_uint32 metadataCount;
77238 drwav_uint8 *pData;
77239 drwav_uint8 *pDataCursor;
77240 drwav_uint64 metadataCursor;
77241 drwav_uint64 extraCapacity;
77242 } drwav__metadata_parser;
77243 DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser)
77244 {
77245 drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity;
77246 if (cap > DRWAV_SIZE_MAX) {
77247 return 0;
77248 }
77249 return (size_t)cap;
77250 }
77251 DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align)
77252 {
77253 drwav_uint8* pResult;
77254 if (align) {
77255 drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align;
77256 if (modulo != 0) {
77257 pParser->pDataCursor += align - modulo;
77258 }
77259 }
77260 pResult = pParser->pDataCursor;
77261 DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser)));
77262 pParser->pDataCursor += size;
77263 return pResult;
77264 }
77265 DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align)
77266 {
77267 size_t extra = bytes + (align ? (align - 1) : 0);
77268 pParser->extraCapacity += extra;
77269 }
77270 DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks)
77271 {
77272 if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
77273 pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
77274 pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
77275 pParser->pDataCursor = pParser->pData;
77276 if (pParser->pData == NULL) {
77277 return DRWAV_OUT_OF_MEMORY;
77278 }
77279 pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1);
77280 pParser->metadataCursor = 0;
77281 }
77282 return DRWAV_SUCCESS;
77283 }
77284 DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
77285 {
77286 if (pCursor != NULL) {
77287 return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
77288 } else {
77289 return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
77290 }
77291 }
77292 DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
77293 {
77294 drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES];
77295 drwav_uint64 totalBytesRead = 0;
77296 size_t bytesJustRead;
77297 if (pMetadata == NULL) {
77298 return 0;
77299 }
77300 bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
77301 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
77302 DRWAV_ASSERT(pChunkHeader != NULL);
77303 if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
77304 drwav_uint32 iSampleLoop;
77305 pMetadata->type = drwav_metadata_type_smpl;
77306 pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
77307 pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4);
77308 pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8);
77309 pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12);
77310 pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16);
77311 pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20);
77312 pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24);
77313 pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28);
77314 pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32);
77315 if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) {
77316 pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
77317 for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
77318 drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES];
77319 bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
77320 if (bytesJustRead == sizeof(smplLoopData)) {
77321 pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0);
77322 pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4);
77323 pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8);
77324 pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12);
77325 pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16);
77326 pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20);
77327 } else {
77328 break;
77329 }
77330 }
77331 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
77332 pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
77333 DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
77334 drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
77335 }
77336 }
77337 }
77338 return totalBytesRead;
77339 }
77340 DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
77341 {
77342 drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES];
77343 drwav_uint64 totalBytesRead = 0;
77344 size_t bytesJustRead;
77345 if (pMetadata == NULL) {
77346 return 0;
77347 }
77348 bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
77349 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
77350 if (bytesJustRead == sizeof(cueHeaderSectionData)) {
77351 pMetadata->type = drwav_metadata_type_cue;
77352 pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData);
77353 if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) {
77354 pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT);
77355 DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
77356 if (pMetadata->data.cue.cuePointCount > 0) {
77357 drwav_uint32 iCuePoint;
77358 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
77359 drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES];
77360 bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
77361 if (bytesJustRead == sizeof(cuePointData)) {
77362 pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0);
77363 pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4);
77364 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
77365 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
77366 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
77367 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
77368 pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12);
77369 pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16);
77370 pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20);
77371 } else {
77372 break;
77373 }
77374 }
77375 }
77376 }
77377 }
77378 return totalBytesRead;
77379 }
77380 DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
77381 {
77382 drwav_uint8 instData[DRWAV_INST_BYTES];
77383 drwav_uint64 bytesRead;
77384 if (pMetadata == NULL) {
77385 return 0;
77386 }
77387 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
77388 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
77389 if (bytesRead == sizeof(instData)) {
77390 pMetadata->type = drwav_metadata_type_inst;
77391 pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0];
77392 pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1];
77393 pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2];
77394 pMetadata->data.inst.lowNote = (drwav_int8)instData[3];
77395 pMetadata->data.inst.highNote = (drwav_int8)instData[4];
77396 pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5];
77397 pMetadata->data.inst.highVelocity = (drwav_int8)instData[6];
77398 }
77399 return bytesRead;
77400 }
77401 DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
77402 {
77403 drwav_uint8 acidData[DRWAV_ACID_BYTES];
77404 drwav_uint64 bytesRead;
77405 if (pMetadata == NULL) {
77406 return 0;
77407 }
77408 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
77409 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
77410 if (bytesRead == sizeof(acidData)) {
77411 pMetadata->type = drwav_metadata_type_acid;
77412 pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0);
77413 pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4);
77414 pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6);
77415 pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8);
77416 pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12);
77417 pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16);
77418 pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18);
77419 pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20);
77420 }
77421 return bytesRead;
77422 }
77423 DRWAV_PRIVATE size_t drwav__strlen(const char* str)
77424 {
77425 size_t result = 0;
77426 while (*str++) {
77427 result += 1;
77428 }
77429 return result;
77430 }
77431 DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead)
77432 {
77433 size_t result = 0;
77434 while (*str++ && result < maxToRead) {
77435 result += 1;
77436 }
77437 return result;
77438 }
77439 DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead)
77440 {
77441 size_t len = drwav__strlen_clamped(str, maxToRead);
77442 if (len) {
77443 char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1);
77444 DRWAV_ASSERT(result != NULL);
77445 DRWAV_COPY_MEMORY(result, str, len);
77446 result[len] = '\0';
77447 return result;
77448 } else {
77449 return NULL;
77450 }
77451 }
77452 typedef struct
77453 {
77454 const void* pBuffer;
77455 size_t sizeInBytes;
77456 size_t cursor;
77457 } drwav_buffer_reader;
77458 DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader)
77459 {
77460 DRWAV_ASSERT(pBuffer != NULL);
77461 DRWAV_ASSERT(pReader != NULL);
77462 DRWAV_ZERO_OBJECT(pReader);
77463 pReader->pBuffer = pBuffer;
77464 pReader->sizeInBytes = sizeInBytes;
77465 pReader->cursor = 0;
77466 return DRWAV_SUCCESS;
77467 }
77468 DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader)
77469 {
77470 DRWAV_ASSERT(pReader != NULL);
77471 return drwav_offset_ptr(pReader->pBuffer, pReader->cursor);
77472 }
77473 DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek)
77474 {
77475 DRWAV_ASSERT(pReader != NULL);
77476 if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {
77477 return DRWAV_BAD_SEEK;
77478 }
77479 pReader->cursor += bytesToSeek;
77480 return DRWAV_SUCCESS;
77481 }
77482 DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)
77483 {
77484 drwav_result result = DRWAV_SUCCESS;
77485 size_t bytesRemaining;
77486 DRWAV_ASSERT(pReader != NULL);
77487 if (pBytesRead != NULL) {
77488 *pBytesRead = 0;
77489 }
77490 bytesRemaining = (pReader->sizeInBytes - pReader->cursor);
77491 if (bytesToRead > bytesRemaining) {
77492 bytesToRead = bytesRemaining;
77493 }
77494 if (pDst == NULL) {
77495 result = drwav_buffer_reader_seek(pReader, bytesToRead);
77496 } else {
77497 DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead);
77498 pReader->cursor += bytesToRead;
77499 }
77500 DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);
77501 if (result == DRWAV_SUCCESS) {
77502 if (pBytesRead != NULL) {
77503 *pBytesRead = bytesToRead;
77504 }
77505 }
77506 return DRWAV_SUCCESS;
77507 }
77508 DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst)
77509 {
77510 drwav_result result;
77511 size_t bytesRead;
77512 drwav_uint8 data[2];
77513 DRWAV_ASSERT(pReader != NULL);
77514 DRWAV_ASSERT(pDst != NULL);
77515 *pDst = 0;
77516 result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
77517 if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
77518 return result;
77519 }
77520 *pDst = drwav_bytes_to_u16(data);
77521 return DRWAV_SUCCESS;
77522 }
77523 DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst)
77524 {
77525 drwav_result result;
77526 size_t bytesRead;
77527 drwav_uint8 data[4];
77528 DRWAV_ASSERT(pReader != NULL);
77529 DRWAV_ASSERT(pDst != NULL);
77530 *pDst = 0;
77531 result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
77532 if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
77533 return result;
77534 }
77535 *pDst = drwav_bytes_to_u32(data);
77536 return DRWAV_SUCCESS;
77537 }
77538 DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
77539 {
77540 drwav_uint8 bextData[DRWAV_BEXT_BYTES];
77541 size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
77542 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
77543 if (bytesRead == sizeof(bextData)) {
77544 drwav_buffer_reader reader;
77545 drwav_uint32 timeReferenceLow;
77546 drwav_uint32 timeReferenceHigh;
77547 size_t extraBytes;
77548 pMetadata->type = drwav_metadata_type_bext;
77549 if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) {
77550 pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES);
77551 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES);
77552 pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
77553 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
77554 pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES);
77555 drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
77556 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);
77557 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);
77558 drwav_buffer_reader_read_u32(&reader, &timeReferenceLow);
77559 drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh);
77560 pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow;
77561 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);
77562 pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1);
77563 drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL);
77564 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);
77565 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);
77566 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);
77567 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);
77568 drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);
77569 DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES));
77570 extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES);
77571 if (extraBytes > 0) {
77572 pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1);
77573 DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
77574 bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
77575 pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory);
77576 } else {
77577 pMetadata->data.bext.pCodingHistory = NULL;
77578 pMetadata->data.bext.codingHistorySize = 0;
77579 }
77580 }
77581 }
77582 return bytesRead;
77583 }
77584 DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type)
77585 {
77586 drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES];
77587 drwav_uint64 totalBytesRead = 0;
77588 size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
77589 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
77590 if (bytesJustRead == sizeof(cueIDBuffer)) {
77591 drwav_uint32 sizeIncludingNullTerminator;
77592 pMetadata->type = type;
77593 pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer);
77594 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
77595 if (sizeIncludingNullTerminator > 0) {
77596 pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
77597 pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
77598 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
77599 drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
77600 } else {
77601 pMetadata->data.labelOrNote.stringLength = 0;
77602 pMetadata->data.labelOrNote.pString = NULL;
77603 }
77604 }
77605 return totalBytesRead;
77606 }
77607 DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
77608 {
77609 drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES];
77610 drwav_uint64 totalBytesRead = 0;
77611 size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
77612 DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
77613 if (bytesJustRead == sizeof(buffer)) {
77614 drwav_uint32 sizeIncludingNullTerminator;
77615 pMetadata->type = drwav_metadata_type_list_labelled_cue_region;
77616 pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0);
77617 pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4);
77618 pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
77619 pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
77620 pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
77621 pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
77622 pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12);
77623 pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14);
77624 pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16);
77625 pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18);
77626 sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
77627 if (sizeIncludingNullTerminator > 0) {
77628 pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
77629 pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
77630 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
77631 drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
77632 } else {
77633 pMetadata->data.labelledCueRegion.stringLength = 0;
77634 pMetadata->data.labelledCueRegion.pString = NULL;
77635 }
77636 }
77637 return totalBytesRead;
77638 }
77639 DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type)
77640 {
77641 drwav_uint64 bytesRead = 0;
77642 drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize;
77643 if (pParser->stage == drwav__metadata_parser_stage_count) {
77644 pParser->metadataCount += 1;
77645 drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
77646 } else {
77647 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
77648 pMetadata->type = type;
77649 if (stringSizeWithNullTerminator > 0) {
77650 pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
77651 pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
77652 DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL);
77653 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
77654 if (bytesRead == chunkSize) {
77655 pParser->metadataCursor += 1;
77656 } else {
77657 }
77658 } else {
77659 pMetadata->data.infoText.stringLength = 0;
77660 pMetadata->data.infoText.pString = NULL;
77661 pParser->metadataCursor += 1;
77662 }
77663 }
77664 return bytesRead;
77665 }
77666 DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location)
77667 {
77668 drwav_uint64 bytesRead = 0;
77669 if (location == drwav_metadata_location_invalid) {
77670 return 0;
77671 }
77672 if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) {
77673 return 0;
77674 }
77675 if (pParser->stage == drwav__metadata_parser_stage_count) {
77676 pParser->metadataCount += 1;
77677 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
77678 } else {
77679 drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
77680 pMetadata->type = drwav_metadata_type_unknown;
77681 pMetadata->data.unknown.chunkLocation = location;
77682 pMetadata->data.unknown.id[0] = pChunkId[0];
77683 pMetadata->data.unknown.id[1] = pChunkId[1];
77684 pMetadata->data.unknown.id[2] = pChunkId[2];
77685 pMetadata->data.unknown.id[3] = pChunkId[3];
77686 pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize;
77687 pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
77688 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
77689 bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
77690 if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
77691 pParser->metadataCursor += 1;
77692 } else {
77693 }
77694 }
77695 return bytesRead;
77696 }
77697 DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID)
77698 {
77699 return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID);
77700 }
77701 DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes)
77702 {
77703 const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc;
77704 drwav_uint64 bytesRead = 0;
77705 if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) {
77706 if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) {
77707 if (pParser->stage == drwav__metadata_parser_stage_count) {
77708 drwav_uint8 buffer[4];
77709 size_t bytesJustRead;
77710 if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) {
77711 return bytesRead;
77712 }
77713 bytesRead += 28;
77714 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
77715 if (bytesJustRead == sizeof(buffer)) {
77716 drwav_uint32 loopCount = drwav_bytes_to_u32(buffer);
77717 drwav_uint64 calculatedLoopCount;
77718 calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
77719 if (calculatedLoopCount == loopCount) {
77720 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
77721 if (bytesJustRead == sizeof(buffer)) {
77722 drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer);
77723 pParser->metadataCount += 1;
77724 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT);
77725 drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
77726 }
77727 } else {
77728 }
77729 }
77730 } else {
77731 bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
77732 if (bytesRead == pChunkHeader->sizeInBytes) {
77733 pParser->metadataCursor += 1;
77734 } else {
77735 }
77736 }
77737 } else {
77738 }
77739 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) {
77740 if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) {
77741 if (pParser->stage == drwav__metadata_parser_stage_count) {
77742 pParser->metadataCount += 1;
77743 } else {
77744 bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
77745 if (bytesRead == pChunkHeader->sizeInBytes) {
77746 pParser->metadataCursor += 1;
77747 } else {
77748 }
77749 }
77750 } else {
77751 }
77752 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) {
77753 if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) {
77754 if (pParser->stage == drwav__metadata_parser_stage_count) {
77755 pParser->metadataCount += 1;
77756 } else {
77757 bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
77758 if (bytesRead == pChunkHeader->sizeInBytes) {
77759 pParser->metadataCursor += 1;
77760 } else {
77761 }
77762 }
77763 } else {
77764 }
77765 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) {
77766 if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) {
77767 if (pParser->stage == drwav__metadata_parser_stage_count) {
77768 size_t cueCount;
77769 pParser->metadataCount += 1;
77770 cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES;
77771 drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT);
77772 } else {
77773 bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
77774 if (bytesRead == pChunkHeader->sizeInBytes) {
77775 pParser->metadataCursor += 1;
77776 } else {
77777 }
77778 }
77779 } else {
77780 }
77781 } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) {
77782 if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) {
77783 if (pParser->stage == drwav__metadata_parser_stage_count) {
77784 char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1];
77785 size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES;
77786 size_t bytesJustRead;
77787 buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0';
77788 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
77789 if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) {
77790 return bytesRead;
77791 }
77792 allocSizeNeeded += drwav__strlen(buffer) + 1;
77793 buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
77794 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
77795 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) {
77796 return bytesRead;
77797 }
77798 allocSizeNeeded += drwav__strlen(buffer) + 1;
77799 buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
77800 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
77801 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) {
77802 return bytesRead;
77803 }
77804 allocSizeNeeded += drwav__strlen(buffer) + 1;
77805 allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES;
77806 drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
77807 pParser->metadataCount += 1;
77808 } else {
77809 bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
77810 if (bytesRead == pChunkHeader->sizeInBytes) {
77811 pParser->metadataCursor += 1;
77812 } else {
77813 }
77814 }
77815 } else {
77816 }
77817 } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) {
77818 drwav_metadata_location listType = drwav_metadata_location_invalid;
77819 while (bytesRead < pChunkHeader->sizeInBytes) {
77820 drwav_uint8 subchunkId[4];
77821 drwav_uint8 subchunkSizeBuffer[4];
77822 drwav_uint64 subchunkDataSize;
77823 drwav_uint64 subchunkBytesRead = 0;
77824 drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
77825 if (bytesJustRead != sizeof(subchunkId)) {
77826 break;
77827 }
77828 if (drwav_fourcc_equal(subchunkId, "adtl")) {
77829 listType = drwav_metadata_location_inside_adtl_list;
77830 continue;
77831 } else if (drwav_fourcc_equal(subchunkId, "INFO")) {
77832 listType = drwav_metadata_location_inside_info_list;
77833 continue;
77834 }
77835 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
77836 if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
77837 break;
77838 }
77839 subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer);
77840 if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) {
77841 if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) {
77842 drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
77843 if (pParser->stage == drwav__metadata_parser_stage_count) {
77844 pParser->metadataCount += 1;
77845 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
77846 } else {
77847 subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note);
77848 if (subchunkBytesRead == subchunkDataSize) {
77849 pParser->metadataCursor += 1;
77850 } else {
77851 }
77852 }
77853 } else {
77854 }
77855 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) {
77856 if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) {
77857 drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
77858 if (pParser->stage == drwav__metadata_parser_stage_count) {
77859 pParser->metadataCount += 1;
77860 drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
77861 } else {
77862 subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
77863 if (subchunkBytesRead == subchunkDataSize) {
77864 pParser->metadataCursor += 1;
77865 } else {
77866 }
77867 }
77868 } else {
77869 }
77870 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) {
77871 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software);
77872 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) {
77873 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright);
77874 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) {
77875 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title);
77876 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) {
77877 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist);
77878 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) {
77879 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment);
77880 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) {
77881 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date);
77882 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) {
77883 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre);
77884 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) {
77885 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album);
77886 } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) {
77887 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber);
77888 } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
77889 subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
77890 }
77891 bytesRead += subchunkBytesRead;
77892 DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
77893 if (subchunkBytesRead < subchunkDataSize) {
77894 drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
77895 if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) {
77896 break;
77897 }
77898 bytesRead += bytesToSeek;
77899 }
77900 if ((subchunkDataSize % 2) == 1) {
77901 if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) {
77902 break;
77903 }
77904 bytesRead += 1;
77905 }
77906 }
77907 } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
77908 bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level);
77909 }
77910 return bytesRead;
77911 }
77912 DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
77913 {
77914 drwav_uint32 bytesPerFrame;
77915 if ((pWav->bitsPerSample & 0x7) == 0) {
77916 bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
77917 } else {
77918 bytesPerFrame = pWav->fmt.blockAlign;
77919 }
77920 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
77921 if (bytesPerFrame != pWav->fmt.channels) {
77922 return 0;
77923 }
77924 }
77925 return bytesPerFrame;
77926 }
77927 DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT)
77928 {
77929 if (pFMT == NULL) {
77930 return 0;
77931 }
77932 if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) {
77933 return pFMT->formatTag;
77934 } else {
77935 return drwav_bytes_to_u16(pFMT->subFormat);
77936 }
77937 }
77938 DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
77939 {
77940 if (pWav == NULL || onRead == NULL || onSeek == NULL) {
77941 return DRWAV_FALSE;
77942 }
77943 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
77944 pWav->onRead = onRead;
77945 pWav->onSeek = onSeek;
77946 pWav->pUserData = pReadSeekUserData;
77947 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
77948 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
77949 return DRWAV_FALSE;
77950 }
77951 return DRWAV_TRUE;
77952 }
77953 DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
77954 {
77955 drwav_uint64 cursor;
77956 drwav_bool32 sequential;
77957 drwav_uint8 riff[4];
77958 drwav_fmt fmt;
77959 unsigned short translatedFormatTag;
77960 drwav_bool32 foundDataChunk;
77961 drwav_uint64 dataChunkSize = 0;
77962 drwav_uint64 sampleCountFromFactChunk = 0;
77963 drwav_uint64 chunkSize;
77964 drwav__metadata_parser metadataParser;
77965 cursor = 0;
77966 sequential = (flags & DRWAV_SEQUENTIAL) != 0;
77967 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
77968 return DRWAV_FALSE;
77969 }
77970 if (drwav_fourcc_equal(riff, "RIFF")) {
77971 pWav->container = drwav_container_riff;
77972 } else if (drwav_fourcc_equal(riff, "riff")) {
77973 int i;
77974 drwav_uint8 riff2[12];
77975 pWav->container = drwav_container_w64;
77976 if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
77977 return DRWAV_FALSE;
77978 }
77979 for (i = 0; i < 12; ++i) {
77980 if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
77981 return DRWAV_FALSE;
77982 }
77983 }
77984 } else if (drwav_fourcc_equal(riff, "RF64")) {
77985 pWav->container = drwav_container_rf64;
77986 } else {
77987 return DRWAV_FALSE;
77988 }
77989 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
77990 drwav_uint8 chunkSizeBytes[4];
77991 drwav_uint8 wave[4];
77992 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
77993 return DRWAV_FALSE;
77994 }
77995 if (pWav->container == drwav_container_riff) {
77996 if (drwav_bytes_to_u32(chunkSizeBytes) < 36) {
77997 return DRWAV_FALSE;
77998 }
77999 } else {
78000 if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) {
78001 return DRWAV_FALSE;
78002 }
78003 }
78004 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
78005 return DRWAV_FALSE;
78006 }
78007 if (!drwav_fourcc_equal(wave, "WAVE")) {
78008 return DRWAV_FALSE;
78009 }
78010 } else {
78011 drwav_uint8 chunkSizeBytes[8];
78012 drwav_uint8 wave[16];
78013 if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
78014 return DRWAV_FALSE;
78015 }
78016 if (drwav_bytes_to_u64(chunkSizeBytes) < 80) {
78017 return DRWAV_FALSE;
78018 }
78019 if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
78020 return DRWAV_FALSE;
78021 }
78022 if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) {
78023 return DRWAV_FALSE;
78024 }
78025 }
78026 if (pWav->container == drwav_container_rf64) {
78027 drwav_uint8 sizeBytes[8];
78028 drwav_uint64 bytesRemainingInChunk;
78029 drwav_chunk_header header;
78030 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
78031 if (result != DRWAV_SUCCESS) {
78032 return DRWAV_FALSE;
78033 }
78034 if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) {
78035 return DRWAV_FALSE;
78036 }
78037 bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
78038 if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
78039 return DRWAV_FALSE;
78040 }
78041 bytesRemainingInChunk -= 8;
78042 cursor += 8;
78043 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
78044 return DRWAV_FALSE;
78045 }
78046 bytesRemainingInChunk -= 8;
78047 dataChunkSize = drwav_bytes_to_u64(sizeBytes);
78048 if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
78049 return DRWAV_FALSE;
78050 }
78051 bytesRemainingInChunk -= 8;
78052 sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes);
78053 if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
78054 return DRWAV_FALSE;
78055 }
78056 cursor += bytesRemainingInChunk;
78057 }
78058 if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) {
78059 return DRWAV_FALSE;
78060 }
78061 if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) ||
78062 (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) ||
78063 (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) ||
78064 fmt.blockAlign == 0) {
78065 return DRWAV_FALSE;
78066 }
78067 translatedFormatTag = fmt.formatTag;
78068 if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
78069 translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0);
78070 }
78071 DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
78072 if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
78073 drwav_uint64 cursorForMetadata = cursor;
78074 metadataParser.onRead = pWav->onRead;
78075 metadataParser.onSeek = pWav->onSeek;
78076 metadataParser.pReadSeekUserData = pWav->pUserData;
78077 metadataParser.stage = drwav__metadata_parser_stage_count;
78078 for (;;) {
78079 drwav_result result;
78080 drwav_uint64 bytesRead;
78081 drwav_uint64 remainingBytes;
78082 drwav_chunk_header header;
78083 result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header);
78084 if (result != DRWAV_SUCCESS) {
78085 break;
78086 }
78087 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes);
78088 DRWAV_ASSERT(bytesRead <= header.sizeInBytes);
78089 remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize;
78090 if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) {
78091 break;
78092 }
78093 cursorForMetadata += remainingBytes;
78094 }
78095 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
78096 return DRWAV_FALSE;
78097 }
78098 drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
78099 metadataParser.stage = drwav__metadata_parser_stage_read;
78100 }
78101 foundDataChunk = DRWAV_FALSE;
78102 for (;;) {
78103 drwav_chunk_header header;
78104 drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
78105 if (result != DRWAV_SUCCESS) {
78106 if (!foundDataChunk) {
78107 return DRWAV_FALSE;
78108 } else {
78109 break;
78110 }
78111 }
78112 if (!sequential && onChunk != NULL) {
78113 drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
78114 if (callbackBytesRead > 0) {
78115 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
78116 return DRWAV_FALSE;
78117 }
78118 }
78119 }
78120 if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
78121 drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes);
78122 if (bytesRead > 0) {
78123 if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
78124 return DRWAV_FALSE;
78125 }
78126 }
78127 }
78128 if (!foundDataChunk) {
78129 pWav->dataChunkDataPos = cursor;
78130 }
78131 chunkSize = header.sizeInBytes;
78132 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
78133 if (drwav_fourcc_equal(header.id.fourcc, "data")) {
78134 foundDataChunk = DRWAV_TRUE;
78135 if (pWav->container != drwav_container_rf64) {
78136 dataChunkSize = chunkSize;
78137 }
78138 }
78139 } else {
78140 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
78141 foundDataChunk = DRWAV_TRUE;
78142 dataChunkSize = chunkSize;
78143 }
78144 }
78145 if (foundDataChunk && sequential) {
78146 break;
78147 }
78148 if (pWav->container == drwav_container_riff) {
78149 if (drwav_fourcc_equal(header.id.fourcc, "fact")) {
78150 drwav_uint32 sampleCount;
78151 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
78152 return DRWAV_FALSE;
78153 }
78154 chunkSize -= 4;
78155 if (!foundDataChunk) {
78156 pWav->dataChunkDataPos = cursor;
78157 }
78158 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
78159 sampleCountFromFactChunk = sampleCount;
78160 } else {
78161 sampleCountFromFactChunk = 0;
78162 }
78163 }
78164 } else if (pWav->container == drwav_container_w64) {
78165 if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
78166 if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
78167 return DRWAV_FALSE;
78168 }
78169 chunkSize -= 8;
78170 if (!foundDataChunk) {
78171 pWav->dataChunkDataPos = cursor;
78172 }
78173 }
78174 } else if (pWav->container == drwav_container_rf64) {
78175 }
78176 chunkSize += header.paddingSize;
78177 if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) {
78178 break;
78179 }
78180 cursor += chunkSize;
78181 if (!foundDataChunk) {
78182 pWav->dataChunkDataPos = cursor;
78183 }
78184 }
78185 pWav->pMetadata = metadataParser.pMetadata;
78186 pWav->metadataCount = metadataParser.metadataCount;
78187 if (!foundDataChunk) {
78188 return DRWAV_FALSE;
78189 }
78190 if (!sequential) {
78191 if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
78192 return DRWAV_FALSE;
78193 }
78194 cursor = pWav->dataChunkDataPos;
78195 }
78196 pWav->fmt = fmt;
78197 pWav->sampleRate = fmt.sampleRate;
78198 pWav->channels = fmt.channels;
78199 pWav->bitsPerSample = fmt.bitsPerSample;
78200 pWav->bytesRemaining = dataChunkSize;
78201 pWav->translatedFormatTag = translatedFormatTag;
78202 pWav->dataChunkDataSize = dataChunkSize;
78203 if (sampleCountFromFactChunk != 0) {
78204 pWav->totalPCMFrameCount = sampleCountFromFactChunk;
78205 } else {
78206 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
78207 if (bytesPerFrame == 0) {
78208 return DRWAV_FALSE;
78209 }
78210 pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;
78211 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
78212 drwav_uint64 totalBlockHeaderSizeInBytes;
78213 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
78214 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
78215 blockCount += 1;
78216 }
78217 totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
78218 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
78219 }
78220 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
78221 drwav_uint64 totalBlockHeaderSizeInBytes;
78222 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
78223 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
78224 blockCount += 1;
78225 }
78226 totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
78227 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
78228 pWav->totalPCMFrameCount += blockCount;
78229 }
78230 }
78231 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
78232 if (pWav->channels > 2) {
78233 return DRWAV_FALSE;
78234 }
78235 }
78236 if (drwav_get_bytes_per_pcm_frame(pWav) == 0) {
78237 return DRWAV_FALSE;
78238 }
78239 #ifdef DR_WAV_LIBSNDFILE_COMPAT
78240 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
78241 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
78242 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;
78243 }
78244 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
78245 drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
78246 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
78247 }
78248 #endif
78249 return DRWAV_TRUE;
78250 }
78251 DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
78252 {
78253 return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
78254 }
78255 DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
78256 {
78257 if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
78258 return DRWAV_FALSE;
78259 }
78260 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
78261 }
78262 DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
78263 {
78264 if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
78265 return DRWAV_FALSE;
78266 }
78267 pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown;
78268 return drwav_init__internal(pWav, NULL, NULL, flags);
78269 }
78270 DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav)
78271 {
78272 drwav_metadata *result = pWav->pMetadata;
78273 pWav->pMetadata = NULL;
78274 pWav->metadataCount = 0;
78275 return result;
78276 }
78277 DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)
78278 {
78279 DRWAV_ASSERT(pWav != NULL);
78280 DRWAV_ASSERT(pWav->onWrite != NULL);
78281 return pWav->onWrite(pWav->pUserData, pData, dataSize);
78282 }
78283 DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte)
78284 {
78285 DRWAV_ASSERT(pWav != NULL);
78286 DRWAV_ASSERT(pWav->onWrite != NULL);
78287 return pWav->onWrite(pWav->pUserData, &byte, 1);
78288 }
78289 DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)
78290 {
78291 DRWAV_ASSERT(pWav != NULL);
78292 DRWAV_ASSERT(pWav->onWrite != NULL);
78293 if (!drwav__is_little_endian()) {
78294 value = drwav__bswap16(value);
78295 }
78296 return drwav__write(pWav, &value, 2);
78297 }
78298 DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)
78299 {
78300 DRWAV_ASSERT(pWav != NULL);
78301 DRWAV_ASSERT(pWav->onWrite != NULL);
78302 if (!drwav__is_little_endian()) {
78303 value = drwav__bswap32(value);
78304 }
78305 return drwav__write(pWav, &value, 4);
78306 }
78307 DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)
78308 {
78309 DRWAV_ASSERT(pWav != NULL);
78310 DRWAV_ASSERT(pWav->onWrite != NULL);
78311 if (!drwav__is_little_endian()) {
78312 value = drwav__bswap64(value);
78313 }
78314 return drwav__write(pWav, &value, 8);
78315 }
78316 DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value)
78317 {
78318 union {
78319 drwav_uint32 u32;
78320 float f32;
78321 } u;
78322 DRWAV_ASSERT(pWav != NULL);
78323 DRWAV_ASSERT(pWav->onWrite != NULL);
78324 u.f32 = value;
78325 if (!drwav__is_little_endian()) {
78326 u.u32 = drwav__bswap32(u.u32);
78327 }
78328 return drwav__write(pWav, &u.u32, 4);
78329 }
78330 DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize)
78331 {
78332 if (pWav == NULL) {
78333 return dataSize;
78334 }
78335 return drwav__write(pWav, pData, dataSize);
78336 }
78337 DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte)
78338 {
78339 if (pWav == NULL) {
78340 return 1;
78341 }
78342 return drwav__write_byte(pWav, byte);
78343 }
78344 DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value)
78345 {
78346 if (pWav == NULL) {
78347 return 2;
78348 }
78349 return drwav__write_u16ne_to_le(pWav, value);
78350 }
78351 DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value)
78352 {
78353 if (pWav == NULL) {
78354 return 4;
78355 }
78356 return drwav__write_u32ne_to_le(pWav, value);
78357 }
78358 #if 0
78359 DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value)
78360 {
78361 if (pWav == NULL) {
78362 return 8;
78363 }
78364 return drwav__write_u64ne_to_le(pWav, value);
78365 }
78366 #endif
78367 DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value)
78368 {
78369 if (pWav == NULL) {
78370 return 4;
78371 }
78372 return drwav__write_f32ne_to_le(pWav, value);
78373 }
78374 DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize)
78375 {
78376 size_t len;
78377 if (pWav == NULL) {
78378 return bufFixedSize;
78379 }
78380 len = drwav__strlen_clamped(str, bufFixedSize);
78381 drwav__write_or_count(pWav, str, len);
78382 if (len < bufFixedSize) {
78383 size_t i;
78384 for (i = 0; i < bufFixedSize - len; ++i) {
78385 drwav__write_byte(pWav, 0);
78386 }
78387 }
78388 return bufFixedSize;
78389 }
78390 DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount)
78391 {
78392 size_t bytesWritten = 0;
78393 drwav_bool32 hasListAdtl = DRWAV_FALSE;
78394 drwav_bool32 hasListInfo = DRWAV_FALSE;
78395 drwav_uint32 iMetadata;
78396 if (pMetadatas == NULL || metadataCount == 0) {
78397 return 0;
78398 }
78399 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
78400 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
78401 drwav_uint32 chunkSize = 0;
78402 if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) {
78403 hasListInfo = DRWAV_TRUE;
78404 }
78405 if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) {
78406 hasListAdtl = DRWAV_TRUE;
78407 }
78408 switch (pMetadata->type) {
78409 case drwav_metadata_type_smpl:
78410 {
78411 drwav_uint32 iLoop;
78412 chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
78413 bytesWritten += drwav__write_or_count(pWav, "smpl", 4);
78414 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78415 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
78416 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
78417 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
78418 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
78419 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
78420 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
78421 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
78422 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
78423 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
78424 for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
78425 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
78426 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
78427 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);
78428 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);
78429 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
78430 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
78431 }
78432 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
78433 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
78434 }
78435 } break;
78436 case drwav_metadata_type_inst:
78437 {
78438 chunkSize = DRWAV_INST_BYTES;
78439 bytesWritten += drwav__write_or_count(pWav, "inst", 4);
78440 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78441 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
78442 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
78443 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
78444 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
78445 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
78446 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
78447 bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
78448 } break;
78449 case drwav_metadata_type_cue:
78450 {
78451 drwav_uint32 iCuePoint;
78452 chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
78453 bytesWritten += drwav__write_or_count(pWav, "cue ", 4);
78454 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78455 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
78456 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
78457 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
78458 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
78459 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
78460 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
78461 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
78462 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);
78463 }
78464 } break;
78465 case drwav_metadata_type_acid:
78466 {
78467 chunkSize = DRWAV_ACID_BYTES;
78468 bytesWritten += drwav__write_or_count(pWav, "acid", 4);
78469 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78470 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
78471 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
78472 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
78473 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
78474 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
78475 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
78476 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
78477 bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
78478 } break;
78479 case drwav_metadata_type_bext:
78480 {
78481 char reservedBuf[DRWAV_BEXT_RESERVED_BYTES];
78482 drwav_uint32 timeReferenceLow;
78483 drwav_uint32 timeReferenceHigh;
78484 chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
78485 bytesWritten += drwav__write_or_count(pWav, "bext", 4);
78486 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78487 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES);
78488 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
78489 bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
78490 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
78491 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
78492 timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
78493 timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32);
78494 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
78495 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
78496 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
78497 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES);
78498 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
78499 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
78500 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
78501 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
78502 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
78503 DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
78504 bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
78505 if (pMetadata->data.bext.codingHistorySize > 0) {
78506 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
78507 }
78508 } break;
78509 case drwav_metadata_type_unknown:
78510 {
78511 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) {
78512 chunkSize = pMetadata->data.unknown.dataSizeInBytes;
78513 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
78514 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78515 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
78516 }
78517 } break;
78518 default: break;
78519 }
78520 if ((chunkSize % 2) != 0) {
78521 bytesWritten += drwav__write_or_count_byte(pWav, 0);
78522 }
78523 }
78524 if (hasListInfo) {
78525 drwav_uint32 chunkSize = 4;
78526 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
78527 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
78528 if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) {
78529 chunkSize += 8;
78530 chunkSize += pMetadata->data.infoText.stringLength + 1;
78531 } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
78532 chunkSize += 8;
78533 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
78534 }
78535 if ((chunkSize % 2) != 0) {
78536 chunkSize += 1;
78537 }
78538 }
78539 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
78540 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78541 bytesWritten += drwav__write_or_count(pWav, "INFO", 4);
78542 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
78543 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
78544 drwav_uint32 subchunkSize = 0;
78545 if (pMetadata->type & drwav_metadata_type_list_all_info_strings) {
78546 const char* pID = NULL;
78547 switch (pMetadata->type) {
78548 case drwav_metadata_type_list_info_software: pID = "ISFT"; break;
78549 case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break;
78550 case drwav_metadata_type_list_info_title: pID = "INAM"; break;
78551 case drwav_metadata_type_list_info_artist: pID = "IART"; break;
78552 case drwav_metadata_type_list_info_comment: pID = "ICMT"; break;
78553 case drwav_metadata_type_list_info_date: pID = "ICRD"; break;
78554 case drwav_metadata_type_list_info_genre: pID = "IGNR"; break;
78555 case drwav_metadata_type_list_info_album: pID = "IPRD"; break;
78556 case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
78557 default: break;
78558 }
78559 DRWAV_ASSERT(pID != NULL);
78560 if (pMetadata->data.infoText.stringLength) {
78561 subchunkSize = pMetadata->data.infoText.stringLength + 1;
78562 bytesWritten += drwav__write_or_count(pWav, pID, 4);
78563 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
78564 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
78565 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
78566 }
78567 } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
78568 if (pMetadata->data.unknown.dataSizeInBytes) {
78569 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
78570 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
78571 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
78572 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
78573 }
78574 }
78575 if ((subchunkSize % 2) != 0) {
78576 bytesWritten += drwav__write_or_count_byte(pWav, 0);
78577 }
78578 }
78579 }
78580 if (hasListAdtl) {
78581 drwav_uint32 chunkSize = 4;
78582 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
78583 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
78584 switch (pMetadata->type)
78585 {
78586 case drwav_metadata_type_list_label:
78587 case drwav_metadata_type_list_note:
78588 {
78589 chunkSize += 8;
78590 chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES;
78591 if (pMetadata->data.labelOrNote.stringLength > 0) {
78592 chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
78593 }
78594 } break;
78595 case drwav_metadata_type_list_labelled_cue_region:
78596 {
78597 chunkSize += 8;
78598 chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES;
78599 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
78600 chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
78601 }
78602 } break;
78603 case drwav_metadata_type_unknown:
78604 {
78605 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
78606 chunkSize += 8;
78607 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
78608 }
78609 } break;
78610 default: break;
78611 }
78612 if ((chunkSize % 2) != 0) {
78613 chunkSize += 1;
78614 }
78615 }
78616 bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
78617 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
78618 bytesWritten += drwav__write_or_count(pWav, "adtl", 4);
78619 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
78620 drwav_metadata* pMetadata = &pMetadatas[iMetadata];
78621 drwav_uint32 subchunkSize = 0;
78622 switch (pMetadata->type)
78623 {
78624 case drwav_metadata_type_list_label:
78625 case drwav_metadata_type_list_note:
78626 {
78627 if (pMetadata->data.labelOrNote.stringLength > 0) {
78628 const char *pID = NULL;
78629 if (pMetadata->type == drwav_metadata_type_list_label) {
78630 pID = "labl";
78631 }
78632 else if (pMetadata->type == drwav_metadata_type_list_note) {
78633 pID = "note";
78634 }
78635 DRWAV_ASSERT(pID != NULL);
78636 DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
78637 subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES;
78638 bytesWritten += drwav__write_or_count(pWav, pID, 4);
78639 subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
78640 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
78641 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
78642 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
78643 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
78644 }
78645 } break;
78646 case drwav_metadata_type_list_labelled_cue_region:
78647 {
78648 subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES;
78649 bytesWritten += drwav__write_or_count(pWav, "ltxt", 4);
78650 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
78651 subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
78652 }
78653 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
78654 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
78655 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
78656 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
78657 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
78658 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
78659 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
78660 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
78661 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
78662 DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
78663 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
78664 bytesWritten += drwav__write_or_count_byte(pWav, '\0');
78665 }
78666 } break;
78667 case drwav_metadata_type_unknown:
78668 {
78669 if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
78670 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
78671 DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
78672 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
78673 bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
78674 bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
78675 }
78676 } break;
78677 default: break;
78678 }
78679 if ((subchunkSize % 2) != 0) {
78680 bytesWritten += drwav__write_or_count_byte(pWav, 0);
78681 }
78682 }
78683 }
78684 DRWAV_ASSERT((bytesWritten % 2) == 0);
78685 return bytesWritten;
78686 }
78687 DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
78688 {
78689 drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
78690 if (chunkSize > 0xFFFFFFFFUL) {
78691 chunkSize = 0xFFFFFFFFUL;
78692 }
78693 return (drwav_uint32)chunkSize;
78694 }
78695 DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)
78696 {
78697 if (dataChunkSize <= 0xFFFFFFFFUL) {
78698 return (drwav_uint32)dataChunkSize;
78699 } else {
78700 return 0xFFFFFFFFUL;
78701 }
78702 }
78703 DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)
78704 {
78705 drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);
78706 return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;
78707 }
78708 DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)
78709 {
78710 return 24 + dataChunkSize;
78711 }
78712 DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata)
78713 {
78714 drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize);
78715 if (chunkSize > 0xFFFFFFFFUL) {
78716 chunkSize = 0xFFFFFFFFUL;
78717 }
78718 return chunkSize;
78719 }
78720 DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize)
78721 {
78722 return dataChunkSize;
78723 }
78724 DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
78725 {
78726 if (pWav == NULL || onWrite == NULL) {
78727 return DRWAV_FALSE;
78728 }
78729 if (!isSequential && onSeek == NULL) {
78730 return DRWAV_FALSE;
78731 }
78732 if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
78733 return DRWAV_FALSE;
78734 }
78735 if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
78736 return DRWAV_FALSE;
78737 }
78738 DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
78739 pWav->onWrite = onWrite;
78740 pWav->onSeek = onSeek;
78741 pWav->pUserData = pUserData;
78742 pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
78743 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
78744 return DRWAV_FALSE;
78745 }
78746 pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
78747 pWav->fmt.channels = (drwav_uint16)pFormat->channels;
78748 pWav->fmt.sampleRate = pFormat->sampleRate;
78749 pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
78750 pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
78751 pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
78752 pWav->fmt.extendedSize = 0;
78753 pWav->isSequentialWrite = isSequential;
78754 return DRWAV_TRUE;
78755 }
78756 DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
78757 {
78758 size_t runningPos = 0;
78759 drwav_uint64 initialDataChunkSize = 0;
78760 drwav_uint64 chunkSizeFMT;
78761 if (pWav->isSequentialWrite) {
78762 initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
78763 if (pFormat->container == drwav_container_riff) {
78764 if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
78765 return DRWAV_FALSE;
78766 }
78767 }
78768 }
78769 pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
78770 if (pFormat->container == drwav_container_riff) {
78771 drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize;
78772 runningPos += drwav__write(pWav, "RIFF", 4);
78773 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
78774 runningPos += drwav__write(pWav, "WAVE", 4);
78775 } else if (pFormat->container == drwav_container_w64) {
78776 drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
78777 runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);
78778 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);
78779 runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);
78780 } else if (pFormat->container == drwav_container_rf64) {
78781 runningPos += drwav__write(pWav, "RF64", 4);
78782 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
78783 runningPos += drwav__write(pWav, "WAVE", 4);
78784 }
78785 if (pFormat->container == drwav_container_rf64) {
78786 drwav_uint32 initialds64ChunkSize = 28;
78787 drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;
78788 runningPos += drwav__write(pWav, "ds64", 4);
78789 runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize);
78790 runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize);
78791 runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize);
78792 runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount);
78793 runningPos += drwav__write_u32ne_to_le(pWav, 0);
78794 }
78795 if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) {
78796 chunkSizeFMT = 16;
78797 runningPos += drwav__write(pWav, "fmt ", 4);
78798 runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);
78799 } else if (pFormat->container == drwav_container_w64) {
78800 chunkSizeFMT = 40;
78801 runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);
78802 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);
78803 }
78804 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
78805 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);
78806 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
78807 runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
78808 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
78809 runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
78810 if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) {
78811 runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
78812 }
78813 pWav->dataChunkDataPos = runningPos;
78814 if (pFormat->container == drwav_container_riff) {
78815 drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
78816 runningPos += drwav__write(pWav, "data", 4);
78817 runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);
78818 } else if (pFormat->container == drwav_container_w64) {
78819 drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
78820 runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);
78821 runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);
78822 } else if (pFormat->container == drwav_container_rf64) {
78823 runningPos += drwav__write(pWav, "data", 4);
78824 runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
78825 }
78826 pWav->container = pFormat->container;
78827 pWav->channels = (drwav_uint16)pFormat->channels;
78828 pWav->sampleRate = pFormat->sampleRate;
78829 pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
78830 pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
78831 pWav->dataChunkDataPos = runningPos;
78832 return DRWAV_TRUE;
78833 }
78834 DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
78835 {
78836 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
78837 return DRWAV_FALSE;
78838 }
78839 return drwav_init_write__internal(pWav, pFormat, 0);
78840 }
78841 DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
78842 {
78843 if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
78844 return DRWAV_FALSE;
78845 }
78846 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
78847 }
78848 DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
78849 {
78850 if (pFormat == NULL) {
78851 return DRWAV_FALSE;
78852 }
78853 return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
78854 }
78855 DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
78856 {
78857 if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
78858 return DRWAV_FALSE;
78859 }
78860 pWav->pMetadata = pMetadata;
78861 pWav->metadataCount = metadataCount;
78862 return drwav_init_write__internal(pWav, pFormat, 0);
78863 }
78864 DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
78865 {
78866 drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
78867 drwav_uint64 riffChunkSizeBytes;
78868 drwav_uint64 fileSizeBytes = 0;
78869 if (pFormat->container == drwav_container_riff) {
78870 riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
78871 fileSizeBytes = (8 + riffChunkSizeBytes);
78872 } else if (pFormat->container == drwav_container_w64) {
78873 riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);
78874 fileSizeBytes = riffChunkSizeBytes;
78875 } else if (pFormat->container == drwav_container_rf64) {
78876 riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
78877 fileSizeBytes = (8 + riffChunkSizeBytes);
78878 }
78879 return fileSizeBytes;
78880 }
78881 #ifndef DR_WAV_NO_STDIO
78882 #include <errno.h>
78883 DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e)
78884 {
78885 switch (e)
78886 {
78887 case 0: return DRWAV_SUCCESS;
78888 #ifdef EPERM
78889 case EPERM: return DRWAV_INVALID_OPERATION;
78890 #endif
78891 #ifdef ENOENT
78892 case ENOENT: return DRWAV_DOES_NOT_EXIST;
78893 #endif
78894 #ifdef ESRCH
78895 case ESRCH: return DRWAV_DOES_NOT_EXIST;
78896 #endif
78897 #ifdef EINTR
78898 case EINTR: return DRWAV_INTERRUPT;
78899 #endif
78900 #ifdef EIO
78901 case EIO: return DRWAV_IO_ERROR;
78902 #endif
78903 #ifdef ENXIO
78904 case ENXIO: return DRWAV_DOES_NOT_EXIST;
78905 #endif
78906 #ifdef E2BIG
78907 case E2BIG: return DRWAV_INVALID_ARGS;
78908 #endif
78909 #ifdef ENOEXEC
78910 case ENOEXEC: return DRWAV_INVALID_FILE;
78911 #endif
78912 #ifdef EBADF
78913 case EBADF: return DRWAV_INVALID_FILE;
78914 #endif
78915 #ifdef ECHILD
78916 case ECHILD: return DRWAV_ERROR;
78917 #endif
78918 #ifdef EAGAIN
78919 case EAGAIN: return DRWAV_UNAVAILABLE;
78920 #endif
78921 #ifdef ENOMEM
78922 case ENOMEM: return DRWAV_OUT_OF_MEMORY;
78923 #endif
78924 #ifdef EACCES
78925 case EACCES: return DRWAV_ACCESS_DENIED;
78926 #endif
78927 #ifdef EFAULT
78928 case EFAULT: return DRWAV_BAD_ADDRESS;
78929 #endif
78930 #ifdef ENOTBLK
78931 case ENOTBLK: return DRWAV_ERROR;
78932 #endif
78933 #ifdef EBUSY
78934 case EBUSY: return DRWAV_BUSY;
78935 #endif
78936 #ifdef EEXIST
78937 case EEXIST: return DRWAV_ALREADY_EXISTS;
78938 #endif
78939 #ifdef EXDEV
78940 case EXDEV: return DRWAV_ERROR;
78941 #endif
78942 #ifdef ENODEV
78943 case ENODEV: return DRWAV_DOES_NOT_EXIST;
78944 #endif
78945 #ifdef ENOTDIR
78946 case ENOTDIR: return DRWAV_NOT_DIRECTORY;
78947 #endif
78948 #ifdef EISDIR
78949 case EISDIR: return DRWAV_IS_DIRECTORY;
78950 #endif
78951 #ifdef EINVAL
78952 case EINVAL: return DRWAV_INVALID_ARGS;
78953 #endif
78954 #ifdef ENFILE
78955 case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES;
78956 #endif
78957 #ifdef EMFILE
78958 case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES;
78959 #endif
78960 #ifdef ENOTTY
78961 case ENOTTY: return DRWAV_INVALID_OPERATION;
78962 #endif
78963 #ifdef ETXTBSY
78964 case ETXTBSY: return DRWAV_BUSY;
78965 #endif
78966 #ifdef EFBIG
78967 case EFBIG: return DRWAV_TOO_BIG;
78968 #endif
78969 #ifdef ENOSPC
78970 case ENOSPC: return DRWAV_NO_SPACE;
78971 #endif
78972 #ifdef ESPIPE
78973 case ESPIPE: return DRWAV_BAD_SEEK;
78974 #endif
78975 #ifdef EROFS
78976 case EROFS: return DRWAV_ACCESS_DENIED;
78977 #endif
78978 #ifdef EMLINK
78979 case EMLINK: return DRWAV_TOO_MANY_LINKS;
78980 #endif
78981 #ifdef EPIPE
78982 case EPIPE: return DRWAV_BAD_PIPE;
78983 #endif
78984 #ifdef EDOM
78985 case EDOM: return DRWAV_OUT_OF_RANGE;
78986 #endif
78987 #ifdef ERANGE
78988 case ERANGE: return DRWAV_OUT_OF_RANGE;
78989 #endif
78990 #ifdef EDEADLK
78991 case EDEADLK: return DRWAV_DEADLOCK;
78992 #endif
78993 #ifdef ENAMETOOLONG
78994 case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG;
78995 #endif
78996 #ifdef ENOLCK
78997 case ENOLCK: return DRWAV_ERROR;
78998 #endif
78999 #ifdef ENOSYS
79000 case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
79001 #endif
79002 #ifdef ENOTEMPTY
79003 case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
79004 #endif
79005 #ifdef ELOOP
79006 case ELOOP: return DRWAV_TOO_MANY_LINKS;
79007 #endif
79008 #ifdef ENOMSG
79009 case ENOMSG: return DRWAV_NO_MESSAGE;
79010 #endif
79011 #ifdef EIDRM
79012 case EIDRM: return DRWAV_ERROR;
79013 #endif
79014 #ifdef ECHRNG
79015 case ECHRNG: return DRWAV_ERROR;
79016 #endif
79017 #ifdef EL2NSYNC
79018 case EL2NSYNC: return DRWAV_ERROR;
79019 #endif
79020 #ifdef EL3HLT
79021 case EL3HLT: return DRWAV_ERROR;
79022 #endif
79023 #ifdef EL3RST
79024 case EL3RST: return DRWAV_ERROR;
79025 #endif
79026 #ifdef ELNRNG
79027 case ELNRNG: return DRWAV_OUT_OF_RANGE;
79028 #endif
79029 #ifdef EUNATCH
79030 case EUNATCH: return DRWAV_ERROR;
79031 #endif
79032 #ifdef ENOCSI
79033 case ENOCSI: return DRWAV_ERROR;
79034 #endif
79035 #ifdef EL2HLT
79036 case EL2HLT: return DRWAV_ERROR;
79037 #endif
79038 #ifdef EBADE
79039 case EBADE: return DRWAV_ERROR;
79040 #endif
79041 #ifdef EBADR
79042 case EBADR: return DRWAV_ERROR;
79043 #endif
79044 #ifdef EXFULL
79045 case EXFULL: return DRWAV_ERROR;
79046 #endif
79047 #ifdef ENOANO
79048 case ENOANO: return DRWAV_ERROR;
79049 #endif
79050 #ifdef EBADRQC
79051 case EBADRQC: return DRWAV_ERROR;
79052 #endif
79053 #ifdef EBADSLT
79054 case EBADSLT: return DRWAV_ERROR;
79055 #endif
79056 #ifdef EBFONT
79057 case EBFONT: return DRWAV_INVALID_FILE;
79058 #endif
79059 #ifdef ENOSTR
79060 case ENOSTR: return DRWAV_ERROR;
79061 #endif
79062 #ifdef ENODATA
79063 case ENODATA: return DRWAV_NO_DATA_AVAILABLE;
79064 #endif
79065 #ifdef ETIME
79066 case ETIME: return DRWAV_TIMEOUT;
79067 #endif
79068 #ifdef ENOSR
79069 case ENOSR: return DRWAV_NO_DATA_AVAILABLE;
79070 #endif
79071 #ifdef ENONET
79072 case ENONET: return DRWAV_NO_NETWORK;
79073 #endif
79074 #ifdef ENOPKG
79075 case ENOPKG: return DRWAV_ERROR;
79076 #endif
79077 #ifdef EREMOTE
79078 case EREMOTE: return DRWAV_ERROR;
79079 #endif
79080 #ifdef ENOLINK
79081 case ENOLINK: return DRWAV_ERROR;
79082 #endif
79083 #ifdef EADV
79084 case EADV: return DRWAV_ERROR;
79085 #endif
79086 #ifdef ESRMNT
79087 case ESRMNT: return DRWAV_ERROR;
79088 #endif
79089 #ifdef ECOMM
79090 case ECOMM: return DRWAV_ERROR;
79091 #endif
79092 #ifdef EPROTO
79093 case EPROTO: return DRWAV_ERROR;
79094 #endif
79095 #ifdef EMULTIHOP
79096 case EMULTIHOP: return DRWAV_ERROR;
79097 #endif
79098 #ifdef EDOTDOT
79099 case EDOTDOT: return DRWAV_ERROR;
79100 #endif
79101 #ifdef EBADMSG
79102 case EBADMSG: return DRWAV_BAD_MESSAGE;
79103 #endif
79104 #ifdef EOVERFLOW
79105 case EOVERFLOW: return DRWAV_TOO_BIG;
79106 #endif
79107 #ifdef ENOTUNIQ
79108 case ENOTUNIQ: return DRWAV_NOT_UNIQUE;
79109 #endif
79110 #ifdef EBADFD
79111 case EBADFD: return DRWAV_ERROR;
79112 #endif
79113 #ifdef EREMCHG
79114 case EREMCHG: return DRWAV_ERROR;
79115 #endif
79116 #ifdef ELIBACC
79117 case ELIBACC: return DRWAV_ACCESS_DENIED;
79118 #endif
79119 #ifdef ELIBBAD
79120 case ELIBBAD: return DRWAV_INVALID_FILE;
79121 #endif
79122 #ifdef ELIBSCN
79123 case ELIBSCN: return DRWAV_INVALID_FILE;
79124 #endif
79125 #ifdef ELIBMAX
79126 case ELIBMAX: return DRWAV_ERROR;
79127 #endif
79128 #ifdef ELIBEXEC
79129 case ELIBEXEC: return DRWAV_ERROR;
79130 #endif
79131 #ifdef EILSEQ
79132 case EILSEQ: return DRWAV_INVALID_DATA;
79133 #endif
79134 #ifdef ERESTART
79135 case ERESTART: return DRWAV_ERROR;
79136 #endif
79137 #ifdef ESTRPIPE
79138 case ESTRPIPE: return DRWAV_ERROR;
79139 #endif
79140 #ifdef EUSERS
79141 case EUSERS: return DRWAV_ERROR;
79142 #endif
79143 #ifdef ENOTSOCK
79144 case ENOTSOCK: return DRWAV_NOT_SOCKET;
79145 #endif
79146 #ifdef EDESTADDRREQ
79147 case EDESTADDRREQ: return DRWAV_NO_ADDRESS;
79148 #endif
79149 #ifdef EMSGSIZE
79150 case EMSGSIZE: return DRWAV_TOO_BIG;
79151 #endif
79152 #ifdef EPROTOTYPE
79153 case EPROTOTYPE: return DRWAV_BAD_PROTOCOL;
79154 #endif
79155 #ifdef ENOPROTOOPT
79156 case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE;
79157 #endif
79158 #ifdef EPROTONOSUPPORT
79159 case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED;
79160 #endif
79161 #ifdef ESOCKTNOSUPPORT
79162 case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED;
79163 #endif
79164 #ifdef EOPNOTSUPP
79165 case EOPNOTSUPP: return DRWAV_INVALID_OPERATION;
79166 #endif
79167 #ifdef EPFNOSUPPORT
79168 case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED;
79169 #endif
79170 #ifdef EAFNOSUPPORT
79171 case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED;
79172 #endif
79173 #ifdef EADDRINUSE
79174 case EADDRINUSE: return DRWAV_ALREADY_IN_USE;
79175 #endif
79176 #ifdef EADDRNOTAVAIL
79177 case EADDRNOTAVAIL: return DRWAV_ERROR;
79178 #endif
79179 #ifdef ENETDOWN
79180 case ENETDOWN: return DRWAV_NO_NETWORK;
79181 #endif
79182 #ifdef ENETUNREACH
79183 case ENETUNREACH: return DRWAV_NO_NETWORK;
79184 #endif
79185 #ifdef ENETRESET
79186 case ENETRESET: return DRWAV_NO_NETWORK;
79187 #endif
79188 #ifdef ECONNABORTED
79189 case ECONNABORTED: return DRWAV_NO_NETWORK;
79190 #endif
79191 #ifdef ECONNRESET
79192 case ECONNRESET: return DRWAV_CONNECTION_RESET;
79193 #endif
79194 #ifdef ENOBUFS
79195 case ENOBUFS: return DRWAV_NO_SPACE;
79196 #endif
79197 #ifdef EISCONN
79198 case EISCONN: return DRWAV_ALREADY_CONNECTED;
79199 #endif
79200 #ifdef ENOTCONN
79201 case ENOTCONN: return DRWAV_NOT_CONNECTED;
79202 #endif
79203 #ifdef ESHUTDOWN
79204 case ESHUTDOWN: return DRWAV_ERROR;
79205 #endif
79206 #ifdef ETOOMANYREFS
79207 case ETOOMANYREFS: return DRWAV_ERROR;
79208 #endif
79209 #ifdef ETIMEDOUT
79210 case ETIMEDOUT: return DRWAV_TIMEOUT;
79211 #endif
79212 #ifdef ECONNREFUSED
79213 case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED;
79214 #endif
79215 #ifdef EHOSTDOWN
79216 case EHOSTDOWN: return DRWAV_NO_HOST;
79217 #endif
79218 #ifdef EHOSTUNREACH
79219 case EHOSTUNREACH: return DRWAV_NO_HOST;
79220 #endif
79221 #ifdef EALREADY
79222 case EALREADY: return DRWAV_IN_PROGRESS;
79223 #endif
79224 #ifdef EINPROGRESS
79225 case EINPROGRESS: return DRWAV_IN_PROGRESS;
79226 #endif
79227 #ifdef ESTALE
79228 case ESTALE: return DRWAV_INVALID_FILE;
79229 #endif
79230 #ifdef EUCLEAN
79231 case EUCLEAN: return DRWAV_ERROR;
79232 #endif
79233 #ifdef ENOTNAM
79234 case ENOTNAM: return DRWAV_ERROR;
79235 #endif
79236 #ifdef ENAVAIL
79237 case ENAVAIL: return DRWAV_ERROR;
79238 #endif
79239 #ifdef EISNAM
79240 case EISNAM: return DRWAV_ERROR;
79241 #endif
79242 #ifdef EREMOTEIO
79243 case EREMOTEIO: return DRWAV_IO_ERROR;
79244 #endif
79245 #ifdef EDQUOT
79246 case EDQUOT: return DRWAV_NO_SPACE;
79247 #endif
79248 #ifdef ENOMEDIUM
79249 case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST;
79250 #endif
79251 #ifdef EMEDIUMTYPE
79252 case EMEDIUMTYPE: return DRWAV_ERROR;
79253 #endif
79254 #ifdef ECANCELED
79255 case ECANCELED: return DRWAV_CANCELLED;
79256 #endif
79257 #ifdef ENOKEY
79258 case ENOKEY: return DRWAV_ERROR;
79259 #endif
79260 #ifdef EKEYEXPIRED
79261 case EKEYEXPIRED: return DRWAV_ERROR;
79262 #endif
79263 #ifdef EKEYREVOKED
79264 case EKEYREVOKED: return DRWAV_ERROR;
79265 #endif
79266 #ifdef EKEYREJECTED
79267 case EKEYREJECTED: return DRWAV_ERROR;
79268 #endif
79269 #ifdef EOWNERDEAD
79270 case EOWNERDEAD: return DRWAV_ERROR;
79271 #endif
79272 #ifdef ENOTRECOVERABLE
79273 case ENOTRECOVERABLE: return DRWAV_ERROR;
79274 #endif
79275 #ifdef ERFKILL
79276 case ERFKILL: return DRWAV_ERROR;
79277 #endif
79278 #ifdef EHWPOISON
79279 case EHWPOISON: return DRWAV_ERROR;
79280 #endif
79281 default: return DRWAV_ERROR;
79282 }
79283 }
79284 DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
79285 {
79286 #if defined(_MSC_VER) && _MSC_VER >= 1400
79287 errno_t err;
79288 #endif
79289 if (ppFile != NULL) {
79290 *ppFile = NULL;
79291 }
79292 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
79293 return DRWAV_INVALID_ARGS;
79294 }
79295 #if defined(_MSC_VER) && _MSC_VER >= 1400
79296 err = fopen_s(ppFile, pFilePath, pOpenMode);
79297 if (err != 0) {
79298 return drwav_result_from_errno(err);
79299 }
79300 #else
79301 #if defined(_WIN32) || defined(__APPLE__)
79302 *ppFile = fopen(pFilePath, pOpenMode);
79303 #else
79304 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
79305 *ppFile = fopen64(pFilePath, pOpenMode);
79306 #else
79307 *ppFile = fopen(pFilePath, pOpenMode);
79308 #endif
79309 #endif
79310 if (*ppFile == NULL) {
79311 drwav_result result = drwav_result_from_errno(errno);
79312 if (result == DRWAV_SUCCESS) {
79313 result = DRWAV_ERROR;
79314 }
79315 return result;
79316 }
79317 #endif
79318 return DRWAV_SUCCESS;
79319 }
79320 #if defined(_WIN32)
79321 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
79322 #define DRWAV_HAS_WFOPEN
79323 #endif
79324 #endif
79325 #ifndef DR_WAV_NO_WCHAR
79326 DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
79327 {
79328 if (ppFile != NULL) {
79329 *ppFile = NULL;
79330 }
79331 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
79332 return DRWAV_INVALID_ARGS;
79333 }
79334 #if defined(DRWAV_HAS_WFOPEN)
79335 {
79336 #if defined(_MSC_VER) && _MSC_VER >= 1400
79337 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
79338 if (err != 0) {
79339 return drwav_result_from_errno(err);
79340 }
79341 #else
79342 *ppFile = _wfopen(pFilePath, pOpenMode);
79343 if (*ppFile == NULL) {
79344 return drwav_result_from_errno(errno);
79345 }
79346 #endif
79347 (void)pAllocationCallbacks;
79348 }
79349 #else
79350 #if defined(__DJGPP__)
79351 {
79352 }
79353 #else
79354 {
79355 mbstate_t mbs;
79356 size_t lenMB;
79357 const wchar_t* pFilePathTemp = pFilePath;
79358 char* pFilePathMB = NULL;
79359 char pOpenModeMB[32] = {0};
79360 DRWAV_ZERO_OBJECT(&mbs);
79361 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
79362 if (lenMB == (size_t)-1) {
79363 return drwav_result_from_errno(errno);
79364 }
79365 pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
79366 if (pFilePathMB == NULL) {
79367 return DRWAV_OUT_OF_MEMORY;
79368 }
79369 pFilePathTemp = pFilePath;
79370 DRWAV_ZERO_OBJECT(&mbs);
79371 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
79372 {
79373 size_t i = 0;
79374 for (;;) {
79375 if (pOpenMode[i] == 0) {
79376 pOpenModeMB[i] = '\0';
79377 break;
79378 }
79379 pOpenModeMB[i] = (char)pOpenMode[i];
79380 i += 1;
79381 }
79382 }
79383 *ppFile = fopen(pFilePathMB, pOpenModeMB);
79384 drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
79385 }
79386 #endif
79387 if (*ppFile == NULL) {
79388 return DRWAV_ERROR;
79389 }
79390 #endif
79391 return DRWAV_SUCCESS;
79392 }
79393 #endif
79394 DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
79395 {
79396 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
79397 }
79398 DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
79399 {
79400 return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
79401 }
79402 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
79403 {
79404 return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
79405 }
79406 DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
79407 {
79408 return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
79409 }
79410 DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks)
79411 {
79412 drwav_bool32 result;
79413 result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
79414 if (result != DRWAV_TRUE) {
79415 fclose(pFile);
79416 return result;
79417 }
79418 pWav->allowedMetadataTypes = allowedMetadataTypes;
79419 result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
79420 if (result != DRWAV_TRUE) {
79421 fclose(pFile);
79422 return result;
79423 }
79424 return DRWAV_TRUE;
79425 }
79426 DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
79427 {
79428 FILE* pFile;
79429 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
79430 return DRWAV_FALSE;
79431 }
79432 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
79433 }
79434 #ifndef DR_WAV_NO_WCHAR
79435 DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
79436 {
79437 return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
79438 }
79439 DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
79440 {
79441 FILE* pFile;
79442 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
79443 return DRWAV_FALSE;
79444 }
79445 return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
79446 }
79447 #endif
79448 DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
79449 {
79450 FILE* pFile;
79451 if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
79452 return DRWAV_FALSE;
79453 }
79454 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
79455 }
79456 #ifndef DR_WAV_NO_WCHAR
79457 DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
79458 {
79459 FILE* pFile;
79460 if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
79461 return DRWAV_FALSE;
79462 }
79463 return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
79464 }
79465 #endif
79466 DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
79467 {
79468 drwav_bool32 result;
79469 result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
79470 if (result != DRWAV_TRUE) {
79471 fclose(pFile);
79472 return result;
79473 }
79474 result = drwav_init_write__internal(pWav, pFormat, totalSampleCount);
79475 if (result != DRWAV_TRUE) {
79476 fclose(pFile);
79477 return result;
79478 }
79479 return DRWAV_TRUE;
79480 }
79481 DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
79482 {
79483 FILE* pFile;
79484 if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) {
79485 return DRWAV_FALSE;
79486 }
79487 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
79488 }
79489 #ifndef DR_WAV_NO_WCHAR
79490 DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
79491 {
79492 FILE* pFile;
79493 if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) {
79494 return DRWAV_FALSE;
79495 }
79496 return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
79497 }
79498 #endif
79499 DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
79500 {
79501 return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
79502 }
79503 DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
79504 {
79505 return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
79506 }
79507 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
79508 {
79509 if (pFormat == NULL) {
79510 return DRWAV_FALSE;
79511 }
79512 return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
79513 }
79514 #ifndef DR_WAV_NO_WCHAR
79515 DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
79516 {
79517 return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
79518 }
79519 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
79520 {
79521 return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
79522 }
79523 DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
79524 {
79525 if (pFormat == NULL) {
79526 return DRWAV_FALSE;
79527 }
79528 return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
79529 }
79530 #endif
79531 #endif
79532 DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
79533 {
79534 drwav* pWav = (drwav*)pUserData;
79535 size_t bytesRemaining;
79536 DRWAV_ASSERT(pWav != NULL);
79537 DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
79538 bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
79539 if (bytesToRead > bytesRemaining) {
79540 bytesToRead = bytesRemaining;
79541 }
79542 if (bytesToRead > 0) {
79543 DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
79544 pWav->memoryStream.currentReadPos += bytesToRead;
79545 }
79546 return bytesToRead;
79547 }
79548 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
79549 {
79550 drwav* pWav = (drwav*)pUserData;
79551 DRWAV_ASSERT(pWav != NULL);
79552 if (origin == drwav_seek_origin_current) {
79553 if (offset > 0) {
79554 if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
79555 return DRWAV_FALSE;
79556 }
79557 } else {
79558 if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
79559 return DRWAV_FALSE;
79560 }
79561 }
79562 pWav->memoryStream.currentReadPos += offset;
79563 } else {
79564 if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
79565 pWav->memoryStream.currentReadPos = offset;
79566 } else {
79567 return DRWAV_FALSE;
79568 }
79569 }
79570 return DRWAV_TRUE;
79571 }
79572 DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
79573 {
79574 drwav* pWav = (drwav*)pUserData;
79575 size_t bytesRemaining;
79576 DRWAV_ASSERT(pWav != NULL);
79577 DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
79578 bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
79579 if (bytesRemaining < bytesToWrite) {
79580 void* pNewData;
79581 size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
79582 if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
79583 newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
79584 }
79585 pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
79586 if (pNewData == NULL) {
79587 return 0;
79588 }
79589 *pWav->memoryStreamWrite.ppData = pNewData;
79590 pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
79591 }
79592 DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
79593 pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
79594 if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
79595 pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
79596 }
79597 *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
79598 return bytesToWrite;
79599 }
79600 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
79601 {
79602 drwav* pWav = (drwav*)pUserData;
79603 DRWAV_ASSERT(pWav != NULL);
79604 if (origin == drwav_seek_origin_current) {
79605 if (offset > 0) {
79606 if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
79607 offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);
79608 }
79609 } else {
79610 if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
79611 offset = -(int)pWav->memoryStreamWrite.currentWritePos;
79612 }
79613 }
79614 pWav->memoryStreamWrite.currentWritePos += offset;
79615 } else {
79616 if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
79617 pWav->memoryStreamWrite.currentWritePos = offset;
79618 } else {
79619 pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize;
79620 }
79621 }
79622 return DRWAV_TRUE;
79623 }
79624 DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
79625 {
79626 return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
79627 }
79628 DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
79629 {
79630 if (data == NULL || dataSize == 0) {
79631 return DRWAV_FALSE;
79632 }
79633 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
79634 return DRWAV_FALSE;
79635 }
79636 pWav->memoryStream.data = (const drwav_uint8*)data;
79637 pWav->memoryStream.dataSize = dataSize;
79638 pWav->memoryStream.currentReadPos = 0;
79639 return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
79640 }
79641 DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
79642 {
79643 if (data == NULL || dataSize == 0) {
79644 return DRWAV_FALSE;
79645 }
79646 if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
79647 return DRWAV_FALSE;
79648 }
79649 pWav->memoryStream.data = (const drwav_uint8*)data;
79650 pWav->memoryStream.dataSize = dataSize;
79651 pWav->memoryStream.currentReadPos = 0;
79652 pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown;
79653 return drwav_init__internal(pWav, NULL, NULL, flags);
79654 }
79655 DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
79656 {
79657 if (ppData == NULL || pDataSize == NULL) {
79658 return DRWAV_FALSE;
79659 }
79660 *ppData = NULL;
79661 *pDataSize = 0;
79662 if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
79663 return DRWAV_FALSE;
79664 }
79665 pWav->memoryStreamWrite.ppData = ppData;
79666 pWav->memoryStreamWrite.pDataSize = pDataSize;
79667 pWav->memoryStreamWrite.dataSize = 0;
79668 pWav->memoryStreamWrite.dataCapacity = 0;
79669 pWav->memoryStreamWrite.currentWritePos = 0;
79670 return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
79671 }
79672 DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
79673 {
79674 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
79675 }
79676 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
79677 {
79678 return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
79679 }
79680 DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
79681 {
79682 if (pFormat == NULL) {
79683 return DRWAV_FALSE;
79684 }
79685 return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
79686 }
79687 DRWAV_API drwav_result drwav_uninit(drwav* pWav)
79688 {
79689 drwav_result result = DRWAV_SUCCESS;
79690 if (pWav == NULL) {
79691 return DRWAV_INVALID_ARGS;
79692 }
79693 if (pWav->onWrite != NULL) {
79694 drwav_uint32 paddingSize = 0;
79695 if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
79696 paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);
79697 } else {
79698 paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);
79699 }
79700 if (paddingSize > 0) {
79701 drwav_uint64 paddingData = 0;
79702 drwav__write(pWav, &paddingData, paddingSize);
79703 }
79704 if (pWav->onSeek && !pWav->isSequentialWrite) {
79705 if (pWav->container == drwav_container_riff) {
79706 if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
79707 drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
79708 drwav__write_u32ne_to_le(pWav, riffChunkSize);
79709 }
79710 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) {
79711 drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
79712 drwav__write_u32ne_to_le(pWav, dataChunkSize);
79713 }
79714 } else if (pWav->container == drwav_container_w64) {
79715 if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
79716 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
79717 drwav__write_u64ne_to_le(pWav, riffChunkSize);
79718 }
79719 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) {
79720 drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
79721 drwav__write_u64ne_to_le(pWav, dataChunkSize);
79722 }
79723 } else if (pWav->container == drwav_container_rf64) {
79724 int ds64BodyPos = 12 + 8;
79725 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {
79726 drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
79727 drwav__write_u64ne_to_le(pWav, riffChunkSize);
79728 }
79729 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {
79730 drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
79731 drwav__write_u64ne_to_le(pWav, dataChunkSize);
79732 }
79733 }
79734 }
79735 if (pWav->isSequentialWrite) {
79736 if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
79737 result = DRWAV_INVALID_FILE;
79738 }
79739 }
79740 } else {
79741 if (pWav->pMetadata != NULL) {
79742 pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData);
79743 }
79744 }
79745 #ifndef DR_WAV_NO_STDIO
79746 if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
79747 fclose((FILE*)pWav->pUserData);
79748 }
79749 #endif
79750 return result;
79751 }
79752 DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
79753 {
79754 size_t bytesRead;
79755 drwav_uint32 bytesPerFrame;
79756 if (pWav == NULL || bytesToRead == 0) {
79757 return 0;
79758 }
79759 if (bytesToRead > pWav->bytesRemaining) {
79760 bytesToRead = (size_t)pWav->bytesRemaining;
79761 }
79762 if (bytesToRead == 0) {
79763 return 0;
79764 }
79765 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
79766 if (bytesPerFrame == 0) {
79767 return 0;
79768 }
79769 if (pBufferOut != NULL) {
79770 bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
79771 } else {
79772 bytesRead = 0;
79773 while (bytesRead < bytesToRead) {
79774 size_t bytesToSeek = (bytesToRead - bytesRead);
79775 if (bytesToSeek > 0x7FFFFFFF) {
79776 bytesToSeek = 0x7FFFFFFF;
79777 }
79778 if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {
79779 break;
79780 }
79781 bytesRead += bytesToSeek;
79782 }
79783 while (bytesRead < bytesToRead) {
79784 drwav_uint8 buffer[4096];
79785 size_t bytesSeeked;
79786 size_t bytesToSeek = (bytesToRead - bytesRead);
79787 if (bytesToSeek > sizeof(buffer)) {
79788 bytesToSeek = sizeof(buffer);
79789 }
79790 bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
79791 bytesRead += bytesSeeked;
79792 if (bytesSeeked < bytesToSeek) {
79793 break;
79794 }
79795 }
79796 }
79797 pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;
79798 pWav->bytesRemaining -= bytesRead;
79799 return bytesRead;
79800 }
79801 DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
79802 {
79803 drwav_uint32 bytesPerFrame;
79804 drwav_uint64 bytesToRead;
79805 if (pWav == NULL || framesToRead == 0) {
79806 return 0;
79807 }
79808 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
79809 return 0;
79810 }
79811 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
79812 if (bytesPerFrame == 0) {
79813 return 0;
79814 }
79815 bytesToRead = framesToRead * bytesPerFrame;
79816 if (bytesToRead > DRWAV_SIZE_MAX) {
79817 bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame;
79818 }
79819 if (bytesToRead == 0) {
79820 return 0;
79821 }
79822 return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
79823 }
79824 DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
79825 {
79826 drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
79827 if (pBufferOut != NULL) {
79828 drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
79829 if (bytesPerFrame == 0) {
79830 return 0;
79831 }
79832 drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag);
79833 }
79834 return framesRead;
79835 }
79836 DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
79837 {
79838 if (drwav__is_little_endian()) {
79839 return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
79840 } else {
79841 return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
79842 }
79843 }
79844 DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
79845 {
79846 if (pWav->onWrite != NULL) {
79847 return DRWAV_FALSE;
79848 }
79849 if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
79850 return DRWAV_FALSE;
79851 }
79852 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
79853 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
79854 DRWAV_ZERO_OBJECT(&pWav->msadpcm);
79855 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
79856 DRWAV_ZERO_OBJECT(&pWav->ima);
79857 } else {
79858 DRWAV_ASSERT(DRWAV_FALSE);
79859 }
79860 }
79861 pWav->readCursorInPCMFrames = 0;
79862 pWav->bytesRemaining = pWav->dataChunkDataSize;
79863 return DRWAV_TRUE;
79864 }
79865 DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
79866 {
79867 if (pWav == NULL || pWav->onSeek == NULL) {
79868 return DRWAV_FALSE;
79869 }
79870 if (pWav->onWrite != NULL) {
79871 return DRWAV_FALSE;
79872 }
79873 if (pWav->totalPCMFrameCount == 0) {
79874 return DRWAV_TRUE;
79875 }
79876 if (targetFrameIndex > pWav->totalPCMFrameCount) {
79877 targetFrameIndex = pWav->totalPCMFrameCount;
79878 }
79879 if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
79880 if (targetFrameIndex < pWav->readCursorInPCMFrames) {
79881 if (!drwav_seek_to_first_pcm_frame(pWav)) {
79882 return DRWAV_FALSE;
79883 }
79884 }
79885 if (targetFrameIndex > pWav->readCursorInPCMFrames) {
79886 drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
79887 drwav_int16 devnull[2048];
79888 while (offsetInFrames > 0) {
79889 drwav_uint64 framesRead = 0;
79890 drwav_uint64 framesToRead = offsetInFrames;
79891 if (framesToRead > drwav_countof(devnull)/pWav->channels) {
79892 framesToRead = drwav_countof(devnull)/pWav->channels;
79893 }
79894 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
79895 framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
79896 } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
79897 framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
79898 } else {
79899 DRWAV_ASSERT(DRWAV_FALSE);
79900 }
79901 if (framesRead != framesToRead) {
79902 return DRWAV_FALSE;
79903 }
79904 offsetInFrames -= framesRead;
79905 }
79906 }
79907 } else {
79908 drwav_uint64 totalSizeInBytes;
79909 drwav_uint64 currentBytePos;
79910 drwav_uint64 targetBytePos;
79911 drwav_uint64 offset;
79912 drwav_uint32 bytesPerFrame;
79913 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
79914 if (bytesPerFrame == 0) {
79915 return DRWAV_FALSE;
79916 }
79917 totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;
79918 DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);
79919 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
79920 targetBytePos = targetFrameIndex * bytesPerFrame;
79921 if (currentBytePos < targetBytePos) {
79922 offset = (targetBytePos - currentBytePos);
79923 } else {
79924 if (!drwav_seek_to_first_pcm_frame(pWav)) {
79925 return DRWAV_FALSE;
79926 }
79927 offset = targetBytePos;
79928 }
79929 while (offset > 0) {
79930 int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
79931 if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
79932 return DRWAV_FALSE;
79933 }
79934 pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;
79935 pWav->bytesRemaining -= offset32;
79936 offset -= offset32;
79937 }
79938 }
79939 return DRWAV_TRUE;
79940 }
79941 DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor)
79942 {
79943 if (pCursor == NULL) {
79944 return DRWAV_INVALID_ARGS;
79945 }
79946 *pCursor = 0;
79947 if (pWav == NULL) {
79948 return DRWAV_INVALID_ARGS;
79949 }
79950 *pCursor = pWav->readCursorInPCMFrames;
79951 return DRWAV_SUCCESS;
79952 }
79953 DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength)
79954 {
79955 if (pLength == NULL) {
79956 return DRWAV_INVALID_ARGS;
79957 }
79958 *pLength = 0;
79959 if (pWav == NULL) {
79960 return DRWAV_INVALID_ARGS;
79961 }
79962 *pLength = pWav->totalPCMFrameCount;
79963 return DRWAV_SUCCESS;
79964 }
79965 DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
79966 {
79967 size_t bytesWritten;
79968 if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
79969 return 0;
79970 }
79971 bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
79972 pWav->dataChunkDataSize += bytesWritten;
79973 return bytesWritten;
79974 }
79975 DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
79976 {
79977 drwav_uint64 bytesToWrite;
79978 drwav_uint64 bytesWritten;
79979 const drwav_uint8* pRunningData;
79980 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
79981 return 0;
79982 }
79983 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
79984 if (bytesToWrite > DRWAV_SIZE_MAX) {
79985 return 0;
79986 }
79987 bytesWritten = 0;
79988 pRunningData = (const drwav_uint8*)pData;
79989 while (bytesToWrite > 0) {
79990 size_t bytesJustWritten;
79991 drwav_uint64 bytesToWriteThisIteration;
79992 bytesToWriteThisIteration = bytesToWrite;
79993 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
79994 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
79995 if (bytesJustWritten == 0) {
79996 break;
79997 }
79998 bytesToWrite -= bytesJustWritten;
79999 bytesWritten += bytesJustWritten;
80000 pRunningData += bytesJustWritten;
80001 }
80002 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
80003 }
80004 DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
80005 {
80006 drwav_uint64 bytesToWrite;
80007 drwav_uint64 bytesWritten;
80008 drwav_uint32 bytesPerSample;
80009 const drwav_uint8* pRunningData;
80010 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
80011 return 0;
80012 }
80013 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
80014 if (bytesToWrite > DRWAV_SIZE_MAX) {
80015 return 0;
80016 }
80017 bytesWritten = 0;
80018 pRunningData = (const drwav_uint8*)pData;
80019 bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
80020 if (bytesPerSample == 0) {
80021 return 0;
80022 }
80023 while (bytesToWrite > 0) {
80024 drwav_uint8 temp[4096];
80025 drwav_uint32 sampleCount;
80026 size_t bytesJustWritten;
80027 drwav_uint64 bytesToWriteThisIteration;
80028 bytesToWriteThisIteration = bytesToWrite;
80029 DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX);
80030 sampleCount = sizeof(temp)/bytesPerSample;
80031 if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) {
80032 bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample;
80033 }
80034 DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
80035 drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag);
80036 bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
80037 if (bytesJustWritten == 0) {
80038 break;
80039 }
80040 bytesToWrite -= bytesJustWritten;
80041 bytesWritten += bytesJustWritten;
80042 pRunningData += bytesJustWritten;
80043 }
80044 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
80045 }
80046 DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
80047 {
80048 if (drwav__is_little_endian()) {
80049 return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);
80050 } else {
80051 return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);
80052 }
80053 }
80054 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80055 {
80056 drwav_uint64 totalFramesRead = 0;
80057 DRWAV_ASSERT(pWav != NULL);
80058 DRWAV_ASSERT(framesToRead > 0);
80059 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80060 DRWAV_ASSERT(framesToRead > 0);
80061 if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
80062 if (pWav->channels == 1) {
80063 drwav_uint8 header[7];
80064 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80065 return totalFramesRead;
80066 }
80067 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80068 pWav->msadpcm.predictor[0] = header[0];
80069 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1);
80070 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3);
80071 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5);
80072 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
80073 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
80074 pWav->msadpcm.cachedFrameCount = 2;
80075 } else {
80076 drwav_uint8 header[14];
80077 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80078 return totalFramesRead;
80079 }
80080 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80081 pWav->msadpcm.predictor[0] = header[0];
80082 pWav->msadpcm.predictor[1] = header[1];
80083 pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2);
80084 pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4);
80085 pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6);
80086 pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8);
80087 pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10);
80088 pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12);
80089 pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
80090 pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
80091 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
80092 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
80093 pWav->msadpcm.cachedFrameCount = 2;
80094 }
80095 }
80096 while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80097 if (pBufferOut != NULL) {
80098 drwav_uint32 iSample = 0;
80099 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
80100 pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
80101 }
80102 pBufferOut += pWav->channels;
80103 }
80104 framesToRead -= 1;
80105 totalFramesRead += 1;
80106 pWav->readCursorInPCMFrames += 1;
80107 pWav->msadpcm.cachedFrameCount -= 1;
80108 }
80109 if (framesToRead == 0) {
80110 break;
80111 }
80112 if (pWav->msadpcm.cachedFrameCount == 0) {
80113 if (pWav->msadpcm.bytesRemainingInBlock == 0) {
80114 continue;
80115 } else {
80116 static drwav_int32 adaptationTable[] = {
80117 230, 230, 230, 230, 307, 409, 512, 614,
80118 768, 614, 512, 409, 307, 230, 230, 230
80119 };
80120 static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
80121 static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
80122 drwav_uint8 nibbles;
80123 drwav_int32 nibble0;
80124 drwav_int32 nibble1;
80125 if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
80126 return totalFramesRead;
80127 }
80128 pWav->msadpcm.bytesRemainingInBlock -= 1;
80129 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
80130 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
80131 if (pWav->channels == 1) {
80132 drwav_int32 newSample0;
80133 drwav_int32 newSample1;
80134 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
80135 newSample0 += nibble0 * pWav->msadpcm.delta[0];
80136 newSample0 = drwav_clamp(newSample0, -32768, 32767);
80137 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
80138 if (pWav->msadpcm.delta[0] < 16) {
80139 pWav->msadpcm.delta[0] = 16;
80140 }
80141 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
80142 pWav->msadpcm.prevFrames[0][1] = newSample0;
80143 newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
80144 newSample1 += nibble1 * pWav->msadpcm.delta[0];
80145 newSample1 = drwav_clamp(newSample1, -32768, 32767);
80146 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
80147 if (pWav->msadpcm.delta[0] < 16) {
80148 pWav->msadpcm.delta[0] = 16;
80149 }
80150 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
80151 pWav->msadpcm.prevFrames[0][1] = newSample1;
80152 pWav->msadpcm.cachedFrames[2] = newSample0;
80153 pWav->msadpcm.cachedFrames[3] = newSample1;
80154 pWav->msadpcm.cachedFrameCount = 2;
80155 } else {
80156 drwav_int32 newSample0;
80157 drwav_int32 newSample1;
80158 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
80159 newSample0 += nibble0 * pWav->msadpcm.delta[0];
80160 newSample0 = drwav_clamp(newSample0, -32768, 32767);
80161 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
80162 if (pWav->msadpcm.delta[0] < 16) {
80163 pWav->msadpcm.delta[0] = 16;
80164 }
80165 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
80166 pWav->msadpcm.prevFrames[0][1] = newSample0;
80167 newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
80168 newSample1 += nibble1 * pWav->msadpcm.delta[1];
80169 newSample1 = drwav_clamp(newSample1, -32768, 32767);
80170 pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
80171 if (pWav->msadpcm.delta[1] < 16) {
80172 pWav->msadpcm.delta[1] = 16;
80173 }
80174 pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
80175 pWav->msadpcm.prevFrames[1][1] = newSample1;
80176 pWav->msadpcm.cachedFrames[2] = newSample0;
80177 pWav->msadpcm.cachedFrames[3] = newSample1;
80178 pWav->msadpcm.cachedFrameCount = 1;
80179 }
80180 }
80181 }
80182 }
80183 return totalFramesRead;
80184 }
80185 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80186 {
80187 drwav_uint64 totalFramesRead = 0;
80188 drwav_uint32 iChannel;
80189 static drwav_int32 indexTable[16] = {
80190 -1, -1, -1, -1, 2, 4, 6, 8,
80191 -1, -1, -1, -1, 2, 4, 6, 8
80192 };
80193 static drwav_int32 stepTable[89] = {
80194 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
80195 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
80196 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
80197 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
80198 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
80199 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
80200 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
80201 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
80202 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
80203 };
80204 DRWAV_ASSERT(pWav != NULL);
80205 DRWAV_ASSERT(framesToRead > 0);
80206 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80207 DRWAV_ASSERT(framesToRead > 0);
80208 if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
80209 if (pWav->channels == 1) {
80210 drwav_uint8 header[4];
80211 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80212 return totalFramesRead;
80213 }
80214 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80215 if (header[2] >= drwav_countof(stepTable)) {
80216 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
80217 pWav->ima.bytesRemainingInBlock = 0;
80218 return totalFramesRead;
80219 }
80220 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
80221 pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1);
80222 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
80223 pWav->ima.cachedFrameCount = 1;
80224 } else {
80225 drwav_uint8 header[8];
80226 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
80227 return totalFramesRead;
80228 }
80229 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
80230 if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
80231 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
80232 pWav->ima.bytesRemainingInBlock = 0;
80233 return totalFramesRead;
80234 }
80235 pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
80236 pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1);
80237 pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4);
80238 pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1);
80239 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
80240 pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
80241 pWav->ima.cachedFrameCount = 1;
80242 }
80243 }
80244 while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
80245 if (pBufferOut != NULL) {
80246 drwav_uint32 iSample;
80247 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
80248 pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
80249 }
80250 pBufferOut += pWav->channels;
80251 }
80252 framesToRead -= 1;
80253 totalFramesRead += 1;
80254 pWav->readCursorInPCMFrames += 1;
80255 pWav->ima.cachedFrameCount -= 1;
80256 }
80257 if (framesToRead == 0) {
80258 break;
80259 }
80260 if (pWav->ima.cachedFrameCount == 0) {
80261 if (pWav->ima.bytesRemainingInBlock == 0) {
80262 continue;
80263 } else {
80264 pWav->ima.cachedFrameCount = 8;
80265 for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
80266 drwav_uint32 iByte;
80267 drwav_uint8 nibbles[4];
80268 if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
80269 pWav->ima.cachedFrameCount = 0;
80270 return totalFramesRead;
80271 }
80272 pWav->ima.bytesRemainingInBlock -= 4;
80273 for (iByte = 0; iByte < 4; ++iByte) {
80274 drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
80275 drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
80276 drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
80277 drwav_int32 predictor = pWav->ima.predictor[iChannel];
80278 drwav_int32 diff = step >> 3;
80279 if (nibble0 & 1) diff += step >> 2;
80280 if (nibble0 & 2) diff += step >> 1;
80281 if (nibble0 & 4) diff += step;
80282 if (nibble0 & 8) diff = -diff;
80283 predictor = drwav_clamp(predictor + diff, -32768, 32767);
80284 pWav->ima.predictor[iChannel] = predictor;
80285 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
80286 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
80287 step = stepTable[pWav->ima.stepIndex[iChannel]];
80288 predictor = pWav->ima.predictor[iChannel];
80289 diff = step >> 3;
80290 if (nibble1 & 1) diff += step >> 2;
80291 if (nibble1 & 2) diff += step >> 1;
80292 if (nibble1 & 4) diff += step;
80293 if (nibble1 & 8) diff = -diff;
80294 predictor = drwav_clamp(predictor + diff, -32768, 32767);
80295 pWav->ima.predictor[iChannel] = predictor;
80296 pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
80297 pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
80298 }
80299 }
80300 }
80301 }
80302 }
80303 return totalFramesRead;
80304 }
80305 #ifndef DR_WAV_NO_CONVERSION_API
80306 static unsigned short g_drwavAlawTable[256] = {
80307 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
80308 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
80309 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
80310 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
80311 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
80312 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
80313 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
80314 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
80315 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
80316 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
80317 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
80318 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
80319 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
80320 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
80321 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
80322 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
80323 };
80324 static unsigned short g_drwavMulawTable[256] = {
80325 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
80326 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
80327 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
80328 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
80329 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
80330 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
80331 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
80332 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
80333 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
80334 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
80335 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
80336 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
80337 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
80338 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
80339 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
80340 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
80341 };
80342 static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
80343 {
80344 return (short)g_drwavAlawTable[sampleIn];
80345 }
80346 static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
80347 {
80348 return (short)g_drwavMulawTable[sampleIn];
80349 }
80350 DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
80351 {
80352 size_t i;
80353 if (bytesPerSample == 1) {
80354 drwav_u8_to_s16(pOut, pIn, totalSampleCount);
80355 return;
80356 }
80357 if (bytesPerSample == 2) {
80358 for (i = 0; i < totalSampleCount; ++i) {
80359 *pOut++ = ((const drwav_int16*)pIn)[i];
80360 }
80361 return;
80362 }
80363 if (bytesPerSample == 3) {
80364 drwav_s24_to_s16(pOut, pIn, totalSampleCount);
80365 return;
80366 }
80367 if (bytesPerSample == 4) {
80368 drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
80369 return;
80370 }
80371 if (bytesPerSample > 8) {
80372 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
80373 return;
80374 }
80375 for (i = 0; i < totalSampleCount; ++i) {
80376 drwav_uint64 sample = 0;
80377 unsigned int shift = (8 - bytesPerSample) * 8;
80378 unsigned int j;
80379 for (j = 0; j < bytesPerSample; j += 1) {
80380 DRWAV_ASSERT(j < 8);
80381 sample |= (drwav_uint64)(pIn[j]) << shift;
80382 shift += 8;
80383 }
80384 pIn += j;
80385 *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
80386 }
80387 }
80388 DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
80389 {
80390 if (bytesPerSample == 4) {
80391 drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
80392 return;
80393 } else if (bytesPerSample == 8) {
80394 drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
80395 return;
80396 } else {
80397 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
80398 return;
80399 }
80400 }
80401 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80402 {
80403 drwav_uint64 totalFramesRead;
80404 drwav_uint8 sampleData[4096] = {0};
80405 drwav_uint32 bytesPerFrame;
80406 drwav_uint32 bytesPerSample;
80407 drwav_uint64 samplesRead;
80408 if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
80409 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
80410 }
80411 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80412 if (bytesPerFrame == 0) {
80413 return 0;
80414 }
80415 bytesPerSample = bytesPerFrame / pWav->channels;
80416 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80417 return 0;
80418 }
80419 totalFramesRead = 0;
80420 while (framesToRead > 0) {
80421 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80422 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80423 if (framesRead == 0) {
80424 break;
80425 }
80426 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80427 samplesRead = framesRead * pWav->channels;
80428 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80429 DRWAV_ASSERT(DRWAV_FALSE);
80430 break;
80431 }
80432 drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
80433 pBufferOut += samplesRead;
80434 framesToRead -= framesRead;
80435 totalFramesRead += framesRead;
80436 }
80437 return totalFramesRead;
80438 }
80439 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80440 {
80441 drwav_uint64 totalFramesRead;
80442 drwav_uint8 sampleData[4096] = {0};
80443 drwav_uint32 bytesPerFrame;
80444 drwav_uint32 bytesPerSample;
80445 drwav_uint64 samplesRead;
80446 if (pBufferOut == NULL) {
80447 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
80448 }
80449 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80450 if (bytesPerFrame == 0) {
80451 return 0;
80452 }
80453 bytesPerSample = bytesPerFrame / pWav->channels;
80454 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80455 return 0;
80456 }
80457 totalFramesRead = 0;
80458 while (framesToRead > 0) {
80459 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80460 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80461 if (framesRead == 0) {
80462 break;
80463 }
80464 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80465 samplesRead = framesRead * pWav->channels;
80466 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80467 DRWAV_ASSERT(DRWAV_FALSE);
80468 break;
80469 }
80470 drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
80471 pBufferOut += samplesRead;
80472 framesToRead -= framesRead;
80473 totalFramesRead += framesRead;
80474 }
80475 return totalFramesRead;
80476 }
80477 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80478 {
80479 drwav_uint64 totalFramesRead;
80480 drwav_uint8 sampleData[4096] = {0};
80481 drwav_uint32 bytesPerFrame;
80482 drwav_uint32 bytesPerSample;
80483 drwav_uint64 samplesRead;
80484 if (pBufferOut == NULL) {
80485 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
80486 }
80487 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80488 if (bytesPerFrame == 0) {
80489 return 0;
80490 }
80491 bytesPerSample = bytesPerFrame / pWav->channels;
80492 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80493 return 0;
80494 }
80495 totalFramesRead = 0;
80496 while (framesToRead > 0) {
80497 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80498 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80499 if (framesRead == 0) {
80500 break;
80501 }
80502 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80503 samplesRead = framesRead * pWav->channels;
80504 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80505 DRWAV_ASSERT(DRWAV_FALSE);
80506 break;
80507 }
80508 drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
80509 pBufferOut += samplesRead;
80510 framesToRead -= framesRead;
80511 totalFramesRead += framesRead;
80512 }
80513 return totalFramesRead;
80514 }
80515 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80516 {
80517 drwav_uint64 totalFramesRead;
80518 drwav_uint8 sampleData[4096] = {0};
80519 drwav_uint32 bytesPerFrame;
80520 drwav_uint32 bytesPerSample;
80521 drwav_uint64 samplesRead;
80522 if (pBufferOut == NULL) {
80523 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
80524 }
80525 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80526 if (bytesPerFrame == 0) {
80527 return 0;
80528 }
80529 bytesPerSample = bytesPerFrame / pWav->channels;
80530 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80531 return 0;
80532 }
80533 totalFramesRead = 0;
80534 while (framesToRead > 0) {
80535 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80536 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80537 if (framesRead == 0) {
80538 break;
80539 }
80540 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80541 samplesRead = framesRead * pWav->channels;
80542 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80543 DRWAV_ASSERT(DRWAV_FALSE);
80544 break;
80545 }
80546 drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
80547 pBufferOut += samplesRead;
80548 framesToRead -= framesRead;
80549 totalFramesRead += framesRead;
80550 }
80551 return totalFramesRead;
80552 }
80553 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80554 {
80555 if (pWav == NULL || framesToRead == 0) {
80556 return 0;
80557 }
80558 if (pBufferOut == NULL) {
80559 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
80560 }
80561 if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
80562 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;
80563 }
80564 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
80565 return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
80566 }
80567 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
80568 return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
80569 }
80570 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
80571 return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
80572 }
80573 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
80574 return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
80575 }
80576 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
80577 return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
80578 }
80579 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
80580 return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
80581 }
80582 return 0;
80583 }
80584 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80585 {
80586 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
80587 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
80588 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
80589 }
80590 return framesRead;
80591 }
80592 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
80593 {
80594 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
80595 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
80596 drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
80597 }
80598 return framesRead;
80599 }
80600 DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
80601 {
80602 int r;
80603 size_t i;
80604 for (i = 0; i < sampleCount; ++i) {
80605 int x = pIn[i];
80606 r = x << 8;
80607 r = r - 32768;
80608 pOut[i] = (short)r;
80609 }
80610 }
80611 DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
80612 {
80613 int r;
80614 size_t i;
80615 for (i = 0; i < sampleCount; ++i) {
80616 int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8;
80617 r = x >> 8;
80618 pOut[i] = (short)r;
80619 }
80620 }
80621 DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
80622 {
80623 int r;
80624 size_t i;
80625 for (i = 0; i < sampleCount; ++i) {
80626 int x = pIn[i];
80627 r = x >> 16;
80628 pOut[i] = (short)r;
80629 }
80630 }
80631 DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
80632 {
80633 int r;
80634 size_t i;
80635 for (i = 0; i < sampleCount; ++i) {
80636 float x = pIn[i];
80637 float c;
80638 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
80639 c = c + 1;
80640 r = (int)(c * 32767.5f);
80641 r = r - 32768;
80642 pOut[i] = (short)r;
80643 }
80644 }
80645 DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
80646 {
80647 int r;
80648 size_t i;
80649 for (i = 0; i < sampleCount; ++i) {
80650 double x = pIn[i];
80651 double c;
80652 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
80653 c = c + 1;
80654 r = (int)(c * 32767.5);
80655 r = r - 32768;
80656 pOut[i] = (short)r;
80657 }
80658 }
80659 DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
80660 {
80661 size_t i;
80662 for (i = 0; i < sampleCount; ++i) {
80663 pOut[i] = drwav__alaw_to_s16(pIn[i]);
80664 }
80665 }
80666 DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
80667 {
80668 size_t i;
80669 for (i = 0; i < sampleCount; ++i) {
80670 pOut[i] = drwav__mulaw_to_s16(pIn[i]);
80671 }
80672 }
80673 DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
80674 {
80675 unsigned int i;
80676 if (bytesPerSample == 1) {
80677 drwav_u8_to_f32(pOut, pIn, sampleCount);
80678 return;
80679 }
80680 if (bytesPerSample == 2) {
80681 drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
80682 return;
80683 }
80684 if (bytesPerSample == 3) {
80685 drwav_s24_to_f32(pOut, pIn, sampleCount);
80686 return;
80687 }
80688 if (bytesPerSample == 4) {
80689 drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
80690 return;
80691 }
80692 if (bytesPerSample > 8) {
80693 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
80694 return;
80695 }
80696 for (i = 0; i < sampleCount; ++i) {
80697 drwav_uint64 sample = 0;
80698 unsigned int shift = (8 - bytesPerSample) * 8;
80699 unsigned int j;
80700 for (j = 0; j < bytesPerSample; j += 1) {
80701 DRWAV_ASSERT(j < 8);
80702 sample |= (drwav_uint64)(pIn[j]) << shift;
80703 shift += 8;
80704 }
80705 pIn += j;
80706 *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
80707 }
80708 }
80709 DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
80710 {
80711 if (bytesPerSample == 4) {
80712 unsigned int i;
80713 for (i = 0; i < sampleCount; ++i) {
80714 *pOut++ = ((const float*)pIn)[i];
80715 }
80716 return;
80717 } else if (bytesPerSample == 8) {
80718 drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
80719 return;
80720 } else {
80721 DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
80722 return;
80723 }
80724 }
80725 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80726 {
80727 drwav_uint64 totalFramesRead;
80728 drwav_uint8 sampleData[4096] = {0};
80729 drwav_uint32 bytesPerFrame;
80730 drwav_uint32 bytesPerSample;
80731 drwav_uint64 samplesRead;
80732 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80733 if (bytesPerFrame == 0) {
80734 return 0;
80735 }
80736 bytesPerSample = bytesPerFrame / pWav->channels;
80737 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80738 return 0;
80739 }
80740 totalFramesRead = 0;
80741 while (framesToRead > 0) {
80742 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80743 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80744 if (framesRead == 0) {
80745 break;
80746 }
80747 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80748 samplesRead = framesRead * pWav->channels;
80749 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80750 DRWAV_ASSERT(DRWAV_FALSE);
80751 break;
80752 }
80753 drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
80754 pBufferOut += samplesRead;
80755 framesToRead -= framesRead;
80756 totalFramesRead += framesRead;
80757 }
80758 return totalFramesRead;
80759 }
80760 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80761 {
80762 drwav_uint64 totalFramesRead;
80763 drwav_int16 samples16[2048];
80764 totalFramesRead = 0;
80765 while (framesToRead > 0) {
80766 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
80767 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
80768 if (framesRead == 0) {
80769 break;
80770 }
80771 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80772 drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
80773 pBufferOut += framesRead*pWav->channels;
80774 framesToRead -= framesRead;
80775 totalFramesRead += framesRead;
80776 }
80777 return totalFramesRead;
80778 }
80779 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80780 {
80781 drwav_uint64 totalFramesRead;
80782 drwav_uint8 sampleData[4096] = {0};
80783 drwav_uint32 bytesPerFrame;
80784 drwav_uint32 bytesPerSample;
80785 drwav_uint64 samplesRead;
80786 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
80787 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
80788 }
80789 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80790 if (bytesPerFrame == 0) {
80791 return 0;
80792 }
80793 bytesPerSample = bytesPerFrame / pWav->channels;
80794 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80795 return 0;
80796 }
80797 totalFramesRead = 0;
80798 while (framesToRead > 0) {
80799 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80800 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80801 if (framesRead == 0) {
80802 break;
80803 }
80804 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80805 samplesRead = framesRead * pWav->channels;
80806 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80807 DRWAV_ASSERT(DRWAV_FALSE);
80808 break;
80809 }
80810 drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
80811 pBufferOut += samplesRead;
80812 framesToRead -= framesRead;
80813 totalFramesRead += framesRead;
80814 }
80815 return totalFramesRead;
80816 }
80817 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80818 {
80819 drwav_uint64 totalFramesRead;
80820 drwav_uint8 sampleData[4096] = {0};
80821 drwav_uint32 bytesPerFrame;
80822 drwav_uint32 bytesPerSample;
80823 drwav_uint64 samplesRead;
80824 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80825 if (bytesPerFrame == 0) {
80826 return 0;
80827 }
80828 bytesPerSample = bytesPerFrame / pWav->channels;
80829 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80830 return 0;
80831 }
80832 totalFramesRead = 0;
80833 while (framesToRead > 0) {
80834 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80835 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80836 if (framesRead == 0) {
80837 break;
80838 }
80839 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80840 samplesRead = framesRead * pWav->channels;
80841 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80842 DRWAV_ASSERT(DRWAV_FALSE);
80843 break;
80844 }
80845 drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
80846 pBufferOut += samplesRead;
80847 framesToRead -= framesRead;
80848 totalFramesRead += framesRead;
80849 }
80850 return totalFramesRead;
80851 }
80852 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80853 {
80854 drwav_uint64 totalFramesRead;
80855 drwav_uint8 sampleData[4096] = {0};
80856 drwav_uint32 bytesPerFrame;
80857 drwav_uint32 bytesPerSample;
80858 drwav_uint64 samplesRead;
80859 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
80860 if (bytesPerFrame == 0) {
80861 return 0;
80862 }
80863 bytesPerSample = bytesPerFrame / pWav->channels;
80864 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
80865 return 0;
80866 }
80867 totalFramesRead = 0;
80868 while (framesToRead > 0) {
80869 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
80870 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
80871 if (framesRead == 0) {
80872 break;
80873 }
80874 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
80875 samplesRead = framesRead * pWav->channels;
80876 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
80877 DRWAV_ASSERT(DRWAV_FALSE);
80878 break;
80879 }
80880 drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
80881 pBufferOut += samplesRead;
80882 framesToRead -= framesRead;
80883 totalFramesRead += framesRead;
80884 }
80885 return totalFramesRead;
80886 }
80887 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80888 {
80889 if (pWav == NULL || framesToRead == 0) {
80890 return 0;
80891 }
80892 if (pBufferOut == NULL) {
80893 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
80894 }
80895 if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {
80896 framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;
80897 }
80898 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
80899 return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
80900 }
80901 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
80902 return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);
80903 }
80904 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
80905 return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
80906 }
80907 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
80908 return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
80909 }
80910 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
80911 return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
80912 }
80913 return 0;
80914 }
80915 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80916 {
80917 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
80918 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
80919 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
80920 }
80921 return framesRead;
80922 }
80923 DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
80924 {
80925 drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
80926 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
80927 drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
80928 }
80929 return framesRead;
80930 }
80931 DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
80932 {
80933 size_t i;
80934 if (pOut == NULL || pIn == NULL) {
80935 return;
80936 }
80937 #ifdef DR_WAV_LIBSNDFILE_COMPAT
80938 for (i = 0; i < sampleCount; ++i) {
80939 *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
80940 }
80941 #else
80942 for (i = 0; i < sampleCount; ++i) {
80943 float x = pIn[i];
80944 x = x * 0.00784313725490196078f;
80945 x = x - 1;
80946 *pOut++ = x;
80947 }
80948 #endif
80949 }
80950 DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
80951 {
80952 size_t i;
80953 if (pOut == NULL || pIn == NULL) {
80954 return;
80955 }
80956 for (i = 0; i < sampleCount; ++i) {
80957 *pOut++ = pIn[i] * 0.000030517578125f;
80958 }
80959 }
80960 DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
80961 {
80962 size_t i;
80963 if (pOut == NULL || pIn == NULL) {
80964 return;
80965 }
80966 for (i = 0; i < sampleCount; ++i) {
80967 double x;
80968 drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8);
80969 drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);
80970 drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);
80971 x = (double)((drwav_int32)(a | b | c) >> 8);
80972 *pOut++ = (float)(x * 0.00000011920928955078125);
80973 }
80974 }
80975 DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
80976 {
80977 size_t i;
80978 if (pOut == NULL || pIn == NULL) {
80979 return;
80980 }
80981 for (i = 0; i < sampleCount; ++i) {
80982 *pOut++ = (float)(pIn[i] / 2147483648.0);
80983 }
80984 }
80985 DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
80986 {
80987 size_t i;
80988 if (pOut == NULL || pIn == NULL) {
80989 return;
80990 }
80991 for (i = 0; i < sampleCount; ++i) {
80992 *pOut++ = (float)pIn[i];
80993 }
80994 }
80995 DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
80996 {
80997 size_t i;
80998 if (pOut == NULL || pIn == NULL) {
80999 return;
81000 }
81001 for (i = 0; i < sampleCount; ++i) {
81002 *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
81003 }
81004 }
81005 DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
81006 {
81007 size_t i;
81008 if (pOut == NULL || pIn == NULL) {
81009 return;
81010 }
81011 for (i = 0; i < sampleCount; ++i) {
81012 *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
81013 }
81014 }
81015 DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
81016 {
81017 unsigned int i;
81018 if (bytesPerSample == 1) {
81019 drwav_u8_to_s32(pOut, pIn, totalSampleCount);
81020 return;
81021 }
81022 if (bytesPerSample == 2) {
81023 drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
81024 return;
81025 }
81026 if (bytesPerSample == 3) {
81027 drwav_s24_to_s32(pOut, pIn, totalSampleCount);
81028 return;
81029 }
81030 if (bytesPerSample == 4) {
81031 for (i = 0; i < totalSampleCount; ++i) {
81032 *pOut++ = ((const drwav_int32*)pIn)[i];
81033 }
81034 return;
81035 }
81036 if (bytesPerSample > 8) {
81037 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
81038 return;
81039 }
81040 for (i = 0; i < totalSampleCount; ++i) {
81041 drwav_uint64 sample = 0;
81042 unsigned int shift = (8 - bytesPerSample) * 8;
81043 unsigned int j;
81044 for (j = 0; j < bytesPerSample; j += 1) {
81045 DRWAV_ASSERT(j < 8);
81046 sample |= (drwav_uint64)(pIn[j]) << shift;
81047 shift += 8;
81048 }
81049 pIn += j;
81050 *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
81051 }
81052 }
81053 DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
81054 {
81055 if (bytesPerSample == 4) {
81056 drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
81057 return;
81058 } else if (bytesPerSample == 8) {
81059 drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
81060 return;
81061 } else {
81062 DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
81063 return;
81064 }
81065 }
81066 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81067 {
81068 drwav_uint64 totalFramesRead;
81069 drwav_uint8 sampleData[4096] = {0};
81070 drwav_uint32 bytesPerFrame;
81071 drwav_uint32 bytesPerSample;
81072 drwav_uint64 samplesRead;
81073 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
81074 return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
81075 }
81076 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
81077 if (bytesPerFrame == 0) {
81078 return 0;
81079 }
81080 bytesPerSample = bytesPerFrame / pWav->channels;
81081 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81082 return 0;
81083 }
81084 totalFramesRead = 0;
81085 while (framesToRead > 0) {
81086 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81087 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81088 if (framesRead == 0) {
81089 break;
81090 }
81091 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
81092 samplesRead = framesRead * pWav->channels;
81093 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81094 DRWAV_ASSERT(DRWAV_FALSE);
81095 break;
81096 }
81097 drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
81098 pBufferOut += samplesRead;
81099 framesToRead -= framesRead;
81100 totalFramesRead += framesRead;
81101 }
81102 return totalFramesRead;
81103 }
81104 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81105 {
81106 drwav_uint64 totalFramesRead = 0;
81107 drwav_int16 samples16[2048];
81108 while (framesToRead > 0) {
81109 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
81110 drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
81111 if (framesRead == 0) {
81112 break;
81113 }
81114 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
81115 drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
81116 pBufferOut += framesRead*pWav->channels;
81117 framesToRead -= framesRead;
81118 totalFramesRead += framesRead;
81119 }
81120 return totalFramesRead;
81121 }
81122 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81123 {
81124 drwav_uint64 totalFramesRead;
81125 drwav_uint8 sampleData[4096] = {0};
81126 drwav_uint32 bytesPerFrame;
81127 drwav_uint32 bytesPerSample;
81128 drwav_uint64 samplesRead;
81129 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
81130 if (bytesPerFrame == 0) {
81131 return 0;
81132 }
81133 bytesPerSample = bytesPerFrame / pWav->channels;
81134 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81135 return 0;
81136 }
81137 totalFramesRead = 0;
81138 while (framesToRead > 0) {
81139 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81140 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81141 if (framesRead == 0) {
81142 break;
81143 }
81144 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
81145 samplesRead = framesRead * pWav->channels;
81146 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81147 DRWAV_ASSERT(DRWAV_FALSE);
81148 break;
81149 }
81150 drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
81151 pBufferOut += samplesRead;
81152 framesToRead -= framesRead;
81153 totalFramesRead += framesRead;
81154 }
81155 return totalFramesRead;
81156 }
81157 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81158 {
81159 drwav_uint64 totalFramesRead;
81160 drwav_uint8 sampleData[4096] = {0};
81161 drwav_uint32 bytesPerFrame;
81162 drwav_uint32 bytesPerSample;
81163 drwav_uint64 samplesRead;
81164 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
81165 if (bytesPerFrame == 0) {
81166 return 0;
81167 }
81168 bytesPerSample = bytesPerFrame / pWav->channels;
81169 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81170 return 0;
81171 }
81172 totalFramesRead = 0;
81173 while (framesToRead > 0) {
81174 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81175 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81176 if (framesRead == 0) {
81177 break;
81178 }
81179 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
81180 samplesRead = framesRead * pWav->channels;
81181 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81182 DRWAV_ASSERT(DRWAV_FALSE);
81183 break;
81184 }
81185 drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
81186 pBufferOut += samplesRead;
81187 framesToRead -= framesRead;
81188 totalFramesRead += framesRead;
81189 }
81190 return totalFramesRead;
81191 }
81192 DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81193 {
81194 drwav_uint64 totalFramesRead;
81195 drwav_uint8 sampleData[4096] = {0};
81196 drwav_uint32 bytesPerFrame;
81197 drwav_uint32 bytesPerSample;
81198 drwav_uint64 samplesRead;
81199 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
81200 if (bytesPerFrame == 0) {
81201 return 0;
81202 }
81203 bytesPerSample = bytesPerFrame / pWav->channels;
81204 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
81205 return 0;
81206 }
81207 totalFramesRead = 0;
81208 while (framesToRead > 0) {
81209 drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
81210 drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
81211 if (framesRead == 0) {
81212 break;
81213 }
81214 DRWAV_ASSERT(framesRead <= framesToReadThisIteration);
81215 samplesRead = framesRead * pWav->channels;
81216 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
81217 DRWAV_ASSERT(DRWAV_FALSE);
81218 break;
81219 }
81220 drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
81221 pBufferOut += samplesRead;
81222 framesToRead -= framesRead;
81223 totalFramesRead += framesRead;
81224 }
81225 return totalFramesRead;
81226 }
81227 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81228 {
81229 if (pWav == NULL || framesToRead == 0) {
81230 return 0;
81231 }
81232 if (pBufferOut == NULL) {
81233 return drwav_read_pcm_frames(pWav, framesToRead, NULL);
81234 }
81235 if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
81236 framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;
81237 }
81238 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
81239 return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
81240 }
81241 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
81242 return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);
81243 }
81244 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
81245 return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
81246 }
81247 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
81248 return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
81249 }
81250 if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
81251 return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
81252 }
81253 return 0;
81254 }
81255 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81256 {
81257 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
81258 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
81259 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
81260 }
81261 return framesRead;
81262 }
81263 DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
81264 {
81265 drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
81266 if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
81267 drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
81268 }
81269 return framesRead;
81270 }
81271 DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
81272 {
81273 size_t i;
81274 if (pOut == NULL || pIn == NULL) {
81275 return;
81276 }
81277 for (i = 0; i < sampleCount; ++i) {
81278 *pOut++ = ((int)pIn[i] - 128) << 24;
81279 }
81280 }
81281 DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
81282 {
81283 size_t i;
81284 if (pOut == NULL || pIn == NULL) {
81285 return;
81286 }
81287 for (i = 0; i < sampleCount; ++i) {
81288 *pOut++ = pIn[i] << 16;
81289 }
81290 }
81291 DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
81292 {
81293 size_t i;
81294 if (pOut == NULL || pIn == NULL) {
81295 return;
81296 }
81297 for (i = 0; i < sampleCount; ++i) {
81298 unsigned int s0 = pIn[i*3 + 0];
81299 unsigned int s1 = pIn[i*3 + 1];
81300 unsigned int s2 = pIn[i*3 + 2];
81301 drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
81302 *pOut++ = sample32;
81303 }
81304 }
81305 DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
81306 {
81307 size_t i;
81308 if (pOut == NULL || pIn == NULL) {
81309 return;
81310 }
81311 for (i = 0; i < sampleCount; ++i) {
81312 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
81313 }
81314 }
81315 DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
81316 {
81317 size_t i;
81318 if (pOut == NULL || pIn == NULL) {
81319 return;
81320 }
81321 for (i = 0; i < sampleCount; ++i) {
81322 *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
81323 }
81324 }
81325 DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
81326 {
81327 size_t i;
81328 if (pOut == NULL || pIn == NULL) {
81329 return;
81330 }
81331 for (i = 0; i < sampleCount; ++i) {
81332 *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
81333 }
81334 }
81335 DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
81336 {
81337 size_t i;
81338 if (pOut == NULL || pIn == NULL) {
81339 return;
81340 }
81341 for (i= 0; i < sampleCount; ++i) {
81342 *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
81343 }
81344 }
81345 DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
81346 {
81347 drwav_uint64 sampleDataSize;
81348 drwav_int16* pSampleData;
81349 drwav_uint64 framesRead;
81350 DRWAV_ASSERT(pWav != NULL);
81351 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
81352 if (sampleDataSize > DRWAV_SIZE_MAX) {
81353 drwav_uninit(pWav);
81354 return NULL;
81355 }
81356 pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
81357 if (pSampleData == NULL) {
81358 drwav_uninit(pWav);
81359 return NULL;
81360 }
81361 framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
81362 if (framesRead != pWav->totalPCMFrameCount) {
81363 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
81364 drwav_uninit(pWav);
81365 return NULL;
81366 }
81367 drwav_uninit(pWav);
81368 if (sampleRate) {
81369 *sampleRate = pWav->sampleRate;
81370 }
81371 if (channels) {
81372 *channels = pWav->channels;
81373 }
81374 if (totalFrameCount) {
81375 *totalFrameCount = pWav->totalPCMFrameCount;
81376 }
81377 return pSampleData;
81378 }
81379 DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
81380 {
81381 drwav_uint64 sampleDataSize;
81382 float* pSampleData;
81383 drwav_uint64 framesRead;
81384 DRWAV_ASSERT(pWav != NULL);
81385 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
81386 if (sampleDataSize > DRWAV_SIZE_MAX) {
81387 drwav_uninit(pWav);
81388 return NULL;
81389 }
81390 pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
81391 if (pSampleData == NULL) {
81392 drwav_uninit(pWav);
81393 return NULL;
81394 }
81395 framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
81396 if (framesRead != pWav->totalPCMFrameCount) {
81397 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
81398 drwav_uninit(pWav);
81399 return NULL;
81400 }
81401 drwav_uninit(pWav);
81402 if (sampleRate) {
81403 *sampleRate = pWav->sampleRate;
81404 }
81405 if (channels) {
81406 *channels = pWav->channels;
81407 }
81408 if (totalFrameCount) {
81409 *totalFrameCount = pWav->totalPCMFrameCount;
81410 }
81411 return pSampleData;
81412 }
81413 DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
81414 {
81415 drwav_uint64 sampleDataSize;
81416 drwav_int32* pSampleData;
81417 drwav_uint64 framesRead;
81418 DRWAV_ASSERT(pWav != NULL);
81419 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
81420 if (sampleDataSize > DRWAV_SIZE_MAX) {
81421 drwav_uninit(pWav);
81422 return NULL;
81423 }
81424 pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
81425 if (pSampleData == NULL) {
81426 drwav_uninit(pWav);
81427 return NULL;
81428 }
81429 framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
81430 if (framesRead != pWav->totalPCMFrameCount) {
81431 drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
81432 drwav_uninit(pWav);
81433 return NULL;
81434 }
81435 drwav_uninit(pWav);
81436 if (sampleRate) {
81437 *sampleRate = pWav->sampleRate;
81438 }
81439 if (channels) {
81440 *channels = pWav->channels;
81441 }
81442 if (totalFrameCount) {
81443 *totalFrameCount = pWav->totalPCMFrameCount;
81444 }
81445 return pSampleData;
81446 }
81447 DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81448 {
81449 drwav wav;
81450 if (channelsOut) {
81451 *channelsOut = 0;
81452 }
81453 if (sampleRateOut) {
81454 *sampleRateOut = 0;
81455 }
81456 if (totalFrameCountOut) {
81457 *totalFrameCountOut = 0;
81458 }
81459 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
81460 return NULL;
81461 }
81462 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81463 }
81464 DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81465 {
81466 drwav wav;
81467 if (channelsOut) {
81468 *channelsOut = 0;
81469 }
81470 if (sampleRateOut) {
81471 *sampleRateOut = 0;
81472 }
81473 if (totalFrameCountOut) {
81474 *totalFrameCountOut = 0;
81475 }
81476 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
81477 return NULL;
81478 }
81479 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81480 }
81481 DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81482 {
81483 drwav wav;
81484 if (channelsOut) {
81485 *channelsOut = 0;
81486 }
81487 if (sampleRateOut) {
81488 *sampleRateOut = 0;
81489 }
81490 if (totalFrameCountOut) {
81491 *totalFrameCountOut = 0;
81492 }
81493 if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
81494 return NULL;
81495 }
81496 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81497 }
81498 #ifndef DR_WAV_NO_STDIO
81499 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81500 {
81501 drwav wav;
81502 if (channelsOut) {
81503 *channelsOut = 0;
81504 }
81505 if (sampleRateOut) {
81506 *sampleRateOut = 0;
81507 }
81508 if (totalFrameCountOut) {
81509 *totalFrameCountOut = 0;
81510 }
81511 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
81512 return NULL;
81513 }
81514 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81515 }
81516 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81517 {
81518 drwav wav;
81519 if (channelsOut) {
81520 *channelsOut = 0;
81521 }
81522 if (sampleRateOut) {
81523 *sampleRateOut = 0;
81524 }
81525 if (totalFrameCountOut) {
81526 *totalFrameCountOut = 0;
81527 }
81528 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
81529 return NULL;
81530 }
81531 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81532 }
81533 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81534 {
81535 drwav wav;
81536 if (channelsOut) {
81537 *channelsOut = 0;
81538 }
81539 if (sampleRateOut) {
81540 *sampleRateOut = 0;
81541 }
81542 if (totalFrameCountOut) {
81543 *totalFrameCountOut = 0;
81544 }
81545 if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
81546 return NULL;
81547 }
81548 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81549 }
81550 #ifndef DR_WAV_NO_WCHAR
81551 DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81552 {
81553 drwav wav;
81554 if (sampleRateOut) {
81555 *sampleRateOut = 0;
81556 }
81557 if (channelsOut) {
81558 *channelsOut = 0;
81559 }
81560 if (totalFrameCountOut) {
81561 *totalFrameCountOut = 0;
81562 }
81563 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
81564 return NULL;
81565 }
81566 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81567 }
81568 DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81569 {
81570 drwav wav;
81571 if (sampleRateOut) {
81572 *sampleRateOut = 0;
81573 }
81574 if (channelsOut) {
81575 *channelsOut = 0;
81576 }
81577 if (totalFrameCountOut) {
81578 *totalFrameCountOut = 0;
81579 }
81580 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
81581 return NULL;
81582 }
81583 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81584 }
81585 DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81586 {
81587 drwav wav;
81588 if (sampleRateOut) {
81589 *sampleRateOut = 0;
81590 }
81591 if (channelsOut) {
81592 *channelsOut = 0;
81593 }
81594 if (totalFrameCountOut) {
81595 *totalFrameCountOut = 0;
81596 }
81597 if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
81598 return NULL;
81599 }
81600 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81601 }
81602 #endif
81603 #endif
81604 DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81605 {
81606 drwav wav;
81607 if (channelsOut) {
81608 *channelsOut = 0;
81609 }
81610 if (sampleRateOut) {
81611 *sampleRateOut = 0;
81612 }
81613 if (totalFrameCountOut) {
81614 *totalFrameCountOut = 0;
81615 }
81616 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
81617 return NULL;
81618 }
81619 return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81620 }
81621 DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81622 {
81623 drwav wav;
81624 if (channelsOut) {
81625 *channelsOut = 0;
81626 }
81627 if (sampleRateOut) {
81628 *sampleRateOut = 0;
81629 }
81630 if (totalFrameCountOut) {
81631 *totalFrameCountOut = 0;
81632 }
81633 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
81634 return NULL;
81635 }
81636 return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81637 }
81638 DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
81639 {
81640 drwav wav;
81641 if (channelsOut) {
81642 *channelsOut = 0;
81643 }
81644 if (sampleRateOut) {
81645 *sampleRateOut = 0;
81646 }
81647 if (totalFrameCountOut) {
81648 *totalFrameCountOut = 0;
81649 }
81650 if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
81651 return NULL;
81652 }
81653 return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
81654 }
81655 #endif
81656 DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
81657 {
81658 if (pAllocationCallbacks != NULL) {
81659 drwav__free_from_callbacks(p, pAllocationCallbacks);
81660 } else {
81661 drwav__free_default(p, NULL);
81662 }
81663 }
81664 DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data)
81665 {
81666 return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8);
81667 }
81668 DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data)
81669 {
81670 return (drwav_int16)drwav_bytes_to_u16(data);
81671 }
81672 DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data)
81673 {
81674 return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24);
81675 }
81676 DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data)
81677 {
81678 union {
81679 drwav_uint32 u32;
81680 float f32;
81681 } value;
81682 value.u32 = drwav_bytes_to_u32(data);
81683 return value.f32;
81684 }
81685 DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data)
81686 {
81687 return (drwav_int32)drwav_bytes_to_u32(data);
81688 }
81689 DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data)
81690 {
81691 return
81692 ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
81693 ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
81694 }
81695 DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data)
81696 {
81697 return (drwav_int64)drwav_bytes_to_u64(data);
81698 }
81699 DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
81700 {
81701 int i;
81702 for (i = 0; i < 16; i += 1) {
81703 if (a[i] != b[i]) {
81704 return DRWAV_FALSE;
81705 }
81706 }
81707 return DRWAV_TRUE;
81708 }
81709 DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
81710 {
81711 return
81712 a[0] == b[0] &&
81713 a[1] == b[1] &&
81714 a[2] == b[2] &&
81715 a[3] == b[3];
81716 }
81717 #ifdef __MRC__
81718 #pragma options opt reset
81719 #endif
81720 #endif
81721 /* dr_wav_c end */
81722 #endif /* DRWAV_IMPLEMENTATION */
81723 #endif /* MA_NO_WAV */
81724
81725 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
81726 #if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
81727 /* dr_flac_c begin */
81728 #ifndef dr_flac_c
81729 #define dr_flac_c
81730 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
81731 #pragma GCC diagnostic push
81732 #if __GNUC__ >= 7
81733 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
81734 #endif
81735 #endif
81736 #ifdef __linux__
81737 #ifndef _BSD_SOURCE
81738 #define _BSD_SOURCE
81739 #endif
81740 #ifndef _DEFAULT_SOURCE
81741 #define _DEFAULT_SOURCE
81742 #endif
81743 #ifndef __USE_BSD
81744 #define __USE_BSD
81745 #endif
81746 #include <endian.h>
81747 #endif
81748 #include <stdlib.h>
81749 #include <string.h>
81750 #ifdef _MSC_VER
81751 #define DRFLAC_INLINE __forceinline
81752 #elif defined(__GNUC__)
81753 #if defined(__STRICT_ANSI__)
81754 #define DRFLAC_GNUC_INLINE_HINT __inline__
81755 #else
81756 #define DRFLAC_GNUC_INLINE_HINT inline
81757 #endif
81758 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
81759 #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline))
81760 #else
81761 #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT
81762 #endif
81763 #elif defined(__WATCOMC__)
81764 #define DRFLAC_INLINE __inline
81765 #else
81766 #define DRFLAC_INLINE
81767 #endif
81768 #if defined(__x86_64__) || defined(_M_X64)
81769 #define DRFLAC_X64
81770 #elif defined(__i386) || defined(_M_IX86)
81771 #define DRFLAC_X86
81772 #elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
81773 #define DRFLAC_ARM
81774 #endif
81775 #if !defined(DR_FLAC_NO_SIMD)
81776 #if defined(DRFLAC_X64) || defined(DRFLAC_X86)
81777 #if defined(_MSC_VER) && !defined(__clang__)
81778 #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2)
81779 #define DRFLAC_SUPPORT_SSE2
81780 #endif
81781 #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41)
81782 #define DRFLAC_SUPPORT_SSE41
81783 #endif
81784 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
81785 #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2)
81786 #define DRFLAC_SUPPORT_SSE2
81787 #endif
81788 #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41)
81789 #define DRFLAC_SUPPORT_SSE41
81790 #endif
81791 #endif
81792 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
81793 #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>)
81794 #define DRFLAC_SUPPORT_SSE2
81795 #endif
81796 #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>)
81797 #define DRFLAC_SUPPORT_SSE41
81798 #endif
81799 #endif
81800 #if defined(DRFLAC_SUPPORT_SSE41)
81801 #include <smmintrin.h>
81802 #elif defined(DRFLAC_SUPPORT_SSE2)
81803 #include <emmintrin.h>
81804 #endif
81805 #endif
81806 #if defined(DRFLAC_ARM)
81807 #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
81808 #define DRFLAC_SUPPORT_NEON
81809 #include <arm_neon.h>
81810 #endif
81811 #endif
81812 #endif
81813 #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
81814 #if defined(_MSC_VER) && !defined(__clang__)
81815 #if _MSC_VER >= 1400
81816 #include <intrin.h>
81817 static void drflac__cpuid(int info[4], int fid)
81818 {
81819 __cpuid(info, fid);
81820 }
81821 #else
81822 #define DRFLAC_NO_CPUID
81823 #endif
81824 #else
81825 #if defined(__GNUC__) || defined(__clang__)
81826 static void drflac__cpuid(int info[4], int fid)
81827 {
81828 #if defined(DRFLAC_X86) && defined(__PIC__)
81829 __asm__ __volatile__ (
81830 "xchg{l} {%%}ebx, %k1;"
81831 "cpuid;"
81832 "xchg{l} {%%}ebx, %k1;"
81833 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
81834 );
81835 #else
81836 __asm__ __volatile__ (
81837 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
81838 );
81839 #endif
81840 }
81841 #else
81842 #define DRFLAC_NO_CPUID
81843 #endif
81844 #endif
81845 #else
81846 #define DRFLAC_NO_CPUID
81847 #endif
81848 static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void)
81849 {
81850 #if defined(DRFLAC_SUPPORT_SSE2)
81851 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2)
81852 #if defined(DRFLAC_X64)
81853 return DRFLAC_TRUE;
81854 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
81855 return DRFLAC_TRUE;
81856 #else
81857 #if defined(DRFLAC_NO_CPUID)
81858 return DRFLAC_FALSE;
81859 #else
81860 int info[4];
81861 drflac__cpuid(info, 1);
81862 return (info[3] & (1 << 26)) != 0;
81863 #endif
81864 #endif
81865 #else
81866 return DRFLAC_FALSE;
81867 #endif
81868 #else
81869 return DRFLAC_FALSE;
81870 #endif
81871 }
81872 static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void)
81873 {
81874 #if defined(DRFLAC_SUPPORT_SSE41)
81875 #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41)
81876 #if defined(__SSE4_1__) || defined(__AVX__)
81877 return DRFLAC_TRUE;
81878 #else
81879 #if defined(DRFLAC_NO_CPUID)
81880 return DRFLAC_FALSE;
81881 #else
81882 int info[4];
81883 drflac__cpuid(info, 1);
81884 return (info[2] & (1 << 19)) != 0;
81885 #endif
81886 #endif
81887 #else
81888 return DRFLAC_FALSE;
81889 #endif
81890 #else
81891 return DRFLAC_FALSE;
81892 #endif
81893 }
81894 #if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__)
81895 #define DRFLAC_HAS_LZCNT_INTRINSIC
81896 #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
81897 #define DRFLAC_HAS_LZCNT_INTRINSIC
81898 #elif defined(__clang__)
81899 #if defined(__has_builtin)
81900 #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
81901 #define DRFLAC_HAS_LZCNT_INTRINSIC
81902 #endif
81903 #endif
81904 #endif
81905 #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
81906 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
81907 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
81908 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
81909 #elif defined(__clang__)
81910 #if defined(__has_builtin)
81911 #if __has_builtin(__builtin_bswap16)
81912 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
81913 #endif
81914 #if __has_builtin(__builtin_bswap32)
81915 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
81916 #endif
81917 #if __has_builtin(__builtin_bswap64)
81918 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
81919 #endif
81920 #endif
81921 #elif defined(__GNUC__)
81922 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
81923 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
81924 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
81925 #endif
81926 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
81927 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
81928 #endif
81929 #elif defined(__WATCOMC__) && defined(__386__)
81930 #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
81931 #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
81932 #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
81933 extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16);
81934 extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32);
81935 extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64);
81936 #pragma aux _watcom_bswap16 = \
81937 "xchg al, ah" \
81938 parm [ax] \
81939 value [ax] \
81940 modify nomemory;
81941 #pragma aux _watcom_bswap32 = \
81942 "bswap eax" \
81943 parm [eax] \
81944 value [eax] \
81945 modify nomemory;
81946 #pragma aux _watcom_bswap64 = \
81947 "bswap eax" \
81948 "bswap edx" \
81949 "xchg eax,edx" \
81950 parm [eax edx] \
81951 value [eax edx] \
81952 modify nomemory;
81953 #endif
81954 #ifndef DRFLAC_ASSERT
81955 #include <assert.h>
81956 #define DRFLAC_ASSERT(expression) assert(expression)
81957 #endif
81958 #ifndef DRFLAC_MALLOC
81959 #define DRFLAC_MALLOC(sz) malloc((sz))
81960 #endif
81961 #ifndef DRFLAC_REALLOC
81962 #define DRFLAC_REALLOC(p, sz) realloc((p), (sz))
81963 #endif
81964 #ifndef DRFLAC_FREE
81965 #define DRFLAC_FREE(p) free((p))
81966 #endif
81967 #ifndef DRFLAC_COPY_MEMORY
81968 #define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
81969 #endif
81970 #ifndef DRFLAC_ZERO_MEMORY
81971 #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
81972 #endif
81973 #ifndef DRFLAC_ZERO_OBJECT
81974 #define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p)))
81975 #endif
81976 #define DRFLAC_MAX_SIMD_VECTOR_SIZE 64
81977 typedef drflac_int32 drflac_result;
81978 #define DRFLAC_SUCCESS 0
81979 #define DRFLAC_ERROR -1
81980 #define DRFLAC_INVALID_ARGS -2
81981 #define DRFLAC_INVALID_OPERATION -3
81982 #define DRFLAC_OUT_OF_MEMORY -4
81983 #define DRFLAC_OUT_OF_RANGE -5
81984 #define DRFLAC_ACCESS_DENIED -6
81985 #define DRFLAC_DOES_NOT_EXIST -7
81986 #define DRFLAC_ALREADY_EXISTS -8
81987 #define DRFLAC_TOO_MANY_OPEN_FILES -9
81988 #define DRFLAC_INVALID_FILE -10
81989 #define DRFLAC_TOO_BIG -11
81990 #define DRFLAC_PATH_TOO_LONG -12
81991 #define DRFLAC_NAME_TOO_LONG -13
81992 #define DRFLAC_NOT_DIRECTORY -14
81993 #define DRFLAC_IS_DIRECTORY -15
81994 #define DRFLAC_DIRECTORY_NOT_EMPTY -16
81995 #define DRFLAC_END_OF_FILE -17
81996 #define DRFLAC_NO_SPACE -18
81997 #define DRFLAC_BUSY -19
81998 #define DRFLAC_IO_ERROR -20
81999 #define DRFLAC_INTERRUPT -21
82000 #define DRFLAC_UNAVAILABLE -22
82001 #define DRFLAC_ALREADY_IN_USE -23
82002 #define DRFLAC_BAD_ADDRESS -24
82003 #define DRFLAC_BAD_SEEK -25
82004 #define DRFLAC_BAD_PIPE -26
82005 #define DRFLAC_DEADLOCK -27
82006 #define DRFLAC_TOO_MANY_LINKS -28
82007 #define DRFLAC_NOT_IMPLEMENTED -29
82008 #define DRFLAC_NO_MESSAGE -30
82009 #define DRFLAC_BAD_MESSAGE -31
82010 #define DRFLAC_NO_DATA_AVAILABLE -32
82011 #define DRFLAC_INVALID_DATA -33
82012 #define DRFLAC_TIMEOUT -34
82013 #define DRFLAC_NO_NETWORK -35
82014 #define DRFLAC_NOT_UNIQUE -36
82015 #define DRFLAC_NOT_SOCKET -37
82016 #define DRFLAC_NO_ADDRESS -38
82017 #define DRFLAC_BAD_PROTOCOL -39
82018 #define DRFLAC_PROTOCOL_UNAVAILABLE -40
82019 #define DRFLAC_PROTOCOL_NOT_SUPPORTED -41
82020 #define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42
82021 #define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43
82022 #define DRFLAC_SOCKET_NOT_SUPPORTED -44
82023 #define DRFLAC_CONNECTION_RESET -45
82024 #define DRFLAC_ALREADY_CONNECTED -46
82025 #define DRFLAC_NOT_CONNECTED -47
82026 #define DRFLAC_CONNECTION_REFUSED -48
82027 #define DRFLAC_NO_HOST -49
82028 #define DRFLAC_IN_PROGRESS -50
82029 #define DRFLAC_CANCELLED -51
82030 #define DRFLAC_MEMORY_ALREADY_MAPPED -52
82031 #define DRFLAC_AT_END -53
82032 #define DRFLAC_CRC_MISMATCH -128
82033 #define DRFLAC_SUBFRAME_CONSTANT 0
82034 #define DRFLAC_SUBFRAME_VERBATIM 1
82035 #define DRFLAC_SUBFRAME_FIXED 8
82036 #define DRFLAC_SUBFRAME_LPC 32
82037 #define DRFLAC_SUBFRAME_RESERVED 255
82038 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
82039 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
82040 #define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
82041 #define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
82042 #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
82043 #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
82044 #define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18
82045 #define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36
82046 #define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12
82047 #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
82048 DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision)
82049 {
82050 if (pMajor) {
82051 *pMajor = DRFLAC_VERSION_MAJOR;
82052 }
82053 if (pMinor) {
82054 *pMinor = DRFLAC_VERSION_MINOR;
82055 }
82056 if (pRevision) {
82057 *pRevision = DRFLAC_VERSION_REVISION;
82058 }
82059 }
82060 DRFLAC_API const char* drflac_version_string(void)
82061 {
82062 return DRFLAC_VERSION_STRING;
82063 }
82064 #if defined(__has_feature)
82065 #if __has_feature(thread_sanitizer)
82066 #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
82067 #else
82068 #define DRFLAC_NO_THREAD_SANITIZE
82069 #endif
82070 #else
82071 #define DRFLAC_NO_THREAD_SANITIZE
82072 #endif
82073 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
82074 static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
82075 #endif
82076 #ifndef DRFLAC_NO_CPUID
82077 static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
82078 static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
82079 DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
82080 {
82081 static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE;
82082 if (!isCPUCapsInitialized) {
82083 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
82084 int info[4] = {0};
82085 drflac__cpuid(info, 0x80000001);
82086 drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
82087 #endif
82088 drflac__gIsSSE2Supported = drflac_has_sse2();
82089 drflac__gIsSSE41Supported = drflac_has_sse41();
82090 isCPUCapsInitialized = DRFLAC_TRUE;
82091 }
82092 }
82093 #else
82094 static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE;
82095 static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void)
82096 {
82097 #if defined(DRFLAC_SUPPORT_NEON)
82098 #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON)
82099 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
82100 return DRFLAC_TRUE;
82101 #else
82102 return DRFLAC_FALSE;
82103 #endif
82104 #else
82105 return DRFLAC_FALSE;
82106 #endif
82107 #else
82108 return DRFLAC_FALSE;
82109 #endif
82110 }
82111 DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void)
82112 {
82113 drflac__gIsNEONSupported = drflac__has_neon();
82114 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
82115 drflac__gIsLZCNTSupported = DRFLAC_TRUE;
82116 #endif
82117 }
82118 #endif
82119 static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void)
82120 {
82121 #if defined(DRFLAC_X86) || defined(DRFLAC_X64)
82122 return DRFLAC_TRUE;
82123 #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
82124 return DRFLAC_TRUE;
82125 #else
82126 int n = 1;
82127 return (*(char*)&n) == 1;
82128 #endif
82129 }
82130 static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
82131 {
82132 #ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
82133 #if defined(_MSC_VER) && !defined(__clang__)
82134 return _byteswap_ushort(n);
82135 #elif defined(__GNUC__) || defined(__clang__)
82136 return __builtin_bswap16(n);
82137 #elif defined(__WATCOMC__) && defined(__386__)
82138 return _watcom_bswap16(n);
82139 #else
82140 #error "This compiler does not support the byte swap intrinsic."
82141 #endif
82142 #else
82143 return ((n & 0xFF00) >> 8) |
82144 ((n & 0x00FF) << 8);
82145 #endif
82146 }
82147 static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
82148 {
82149 #ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
82150 #if defined(_MSC_VER) && !defined(__clang__)
82151 return _byteswap_ulong(n);
82152 #elif defined(__GNUC__) || defined(__clang__)
82153 #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT)
82154 drflac_uint32 r;
82155 __asm__ __volatile__ (
82156 #if defined(DRFLAC_64BIT)
82157 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
82158 #else
82159 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
82160 #endif
82161 );
82162 return r;
82163 #else
82164 return __builtin_bswap32(n);
82165 #endif
82166 #elif defined(__WATCOMC__) && defined(__386__)
82167 return _watcom_bswap32(n);
82168 #else
82169 #error "This compiler does not support the byte swap intrinsic."
82170 #endif
82171 #else
82172 return ((n & 0xFF000000) >> 24) |
82173 ((n & 0x00FF0000) >> 8) |
82174 ((n & 0x0000FF00) << 8) |
82175 ((n & 0x000000FF) << 24);
82176 #endif
82177 }
82178 static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
82179 {
82180 #ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
82181 #if defined(_MSC_VER) && !defined(__clang__)
82182 return _byteswap_uint64(n);
82183 #elif defined(__GNUC__) || defined(__clang__)
82184 return __builtin_bswap64(n);
82185 #elif defined(__WATCOMC__) && defined(__386__)
82186 return _watcom_bswap64(n);
82187 #else
82188 #error "This compiler does not support the byte swap intrinsic."
82189 #endif
82190 #else
82191 return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) |
82192 ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) |
82193 ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) |
82194 ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) |
82195 ((n & ((drflac_uint64)0xFF000000 )) << 8) |
82196 ((n & ((drflac_uint64)0x00FF0000 )) << 24) |
82197 ((n & ((drflac_uint64)0x0000FF00 )) << 40) |
82198 ((n & ((drflac_uint64)0x000000FF )) << 56);
82199 #endif
82200 }
82201 static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n)
82202 {
82203 if (drflac__is_little_endian()) {
82204 return drflac__swap_endian_uint16(n);
82205 }
82206 return n;
82207 }
82208 static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
82209 {
82210 if (drflac__is_little_endian()) {
82211 return drflac__swap_endian_uint32(n);
82212 }
82213 return n;
82214 }
82215 static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData)
82216 {
82217 const drflac_uint8* pNum = (drflac_uint8*)pData;
82218 return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3);
82219 }
82220 static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
82221 {
82222 if (drflac__is_little_endian()) {
82223 return drflac__swap_endian_uint64(n);
82224 }
82225 return n;
82226 }
82227 static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
82228 {
82229 if (!drflac__is_little_endian()) {
82230 return drflac__swap_endian_uint32(n);
82231 }
82232 return n;
82233 }
82234 static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData)
82235 {
82236 const drflac_uint8* pNum = (drflac_uint8*)pData;
82237 return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24;
82238 }
82239 static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n)
82240 {
82241 drflac_uint32 result = 0;
82242 result |= (n & 0x7F000000) >> 3;
82243 result |= (n & 0x007F0000) >> 2;
82244 result |= (n & 0x00007F00) >> 1;
82245 result |= (n & 0x0000007F) >> 0;
82246 return result;
82247 }
82248 static drflac_uint8 drflac__crc8_table[] = {
82249 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
82250 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
82251 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
82252 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
82253 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
82254 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
82255 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
82256 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
82257 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
82258 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
82259 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
82260 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
82261 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
82262 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
82263 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
82264 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
82265 };
82266 static drflac_uint16 drflac__crc16_table[] = {
82267 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
82268 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
82269 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
82270 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
82271 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
82272 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
82273 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
82274 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
82275 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
82276 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
82277 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
82278 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
82279 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
82280 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
82281 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
82282 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
82283 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
82284 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
82285 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
82286 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
82287 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
82288 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
82289 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
82290 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
82291 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
82292 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
82293 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
82294 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
82295 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
82296 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
82297 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
82298 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
82299 };
82300 static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data)
82301 {
82302 return drflac__crc8_table[crc ^ data];
82303 }
82304 static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count)
82305 {
82306 #ifdef DR_FLAC_NO_CRC
82307 (void)crc;
82308 (void)data;
82309 (void)count;
82310 return 0;
82311 #else
82312 #if 0
82313 drflac_uint8 p = 0x07;
82314 for (int i = count-1; i >= 0; --i) {
82315 drflac_uint8 bit = (data & (1 << i)) >> i;
82316 if (crc & 0x80) {
82317 crc = ((crc << 1) | bit) ^ p;
82318 } else {
82319 crc = ((crc << 1) | bit);
82320 }
82321 }
82322 return crc;
82323 #else
82324 drflac_uint32 wholeBytes;
82325 drflac_uint32 leftoverBits;
82326 drflac_uint64 leftoverDataMask;
82327 static drflac_uint64 leftoverDataMaskTable[8] = {
82328 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
82329 };
82330 DRFLAC_ASSERT(count <= 32);
82331 wholeBytes = count >> 3;
82332 leftoverBits = count - (wholeBytes*8);
82333 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
82334 switch (wholeBytes) {
82335 case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
82336 case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
82337 case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
82338 case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
82339 case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);
82340 }
82341 return crc;
82342 #endif
82343 #endif
82344 }
82345 static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data)
82346 {
82347 return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data];
82348 }
82349 static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data)
82350 {
82351 #ifdef DRFLAC_64BIT
82352 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
82353 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
82354 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
82355 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
82356 #endif
82357 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
82358 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
82359 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
82360 crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
82361 return crc;
82362 }
82363 static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount)
82364 {
82365 switch (byteCount)
82366 {
82367 #ifdef DRFLAC_64BIT
82368 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
82369 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
82370 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
82371 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
82372 #endif
82373 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
82374 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
82375 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
82376 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
82377 }
82378 return crc;
82379 }
82380 #if 0
82381 static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
82382 {
82383 #ifdef DR_FLAC_NO_CRC
82384 (void)crc;
82385 (void)data;
82386 (void)count;
82387 return 0;
82388 #else
82389 #if 0
82390 drflac_uint16 p = 0x8005;
82391 for (int i = count-1; i >= 0; --i) {
82392 drflac_uint16 bit = (data & (1ULL << i)) >> i;
82393 if (r & 0x8000) {
82394 r = ((r << 1) | bit) ^ p;
82395 } else {
82396 r = ((r << 1) | bit);
82397 }
82398 }
82399 return crc;
82400 #else
82401 drflac_uint32 wholeBytes;
82402 drflac_uint32 leftoverBits;
82403 drflac_uint64 leftoverDataMask;
82404 static drflac_uint64 leftoverDataMaskTable[8] = {
82405 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
82406 };
82407 DRFLAC_ASSERT(count <= 64);
82408 wholeBytes = count >> 3;
82409 leftoverBits = count & 7;
82410 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
82411 switch (wholeBytes) {
82412 default:
82413 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
82414 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
82415 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
82416 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
82417 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
82418 }
82419 return crc;
82420 #endif
82421 #endif
82422 }
82423 static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count)
82424 {
82425 #ifdef DR_FLAC_NO_CRC
82426 (void)crc;
82427 (void)data;
82428 (void)count;
82429 return 0;
82430 #else
82431 drflac_uint32 wholeBytes;
82432 drflac_uint32 leftoverBits;
82433 drflac_uint64 leftoverDataMask;
82434 static drflac_uint64 leftoverDataMaskTable[8] = {
82435 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
82436 };
82437 DRFLAC_ASSERT(count <= 64);
82438 wholeBytes = count >> 3;
82439 leftoverBits = count & 7;
82440 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
82441 switch (wholeBytes) {
82442 default:
82443 case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));
82444 case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
82445 case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
82446 case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
82447 case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
82448 case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
82449 case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
82450 case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
82451 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
82452 }
82453 return crc;
82454 #endif
82455 }
82456 static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count)
82457 {
82458 #ifdef DRFLAC_64BIT
82459 return drflac_crc16__64bit(crc, data, count);
82460 #else
82461 return drflac_crc16__32bit(crc, data, count);
82462 #endif
82463 }
82464 #endif
82465 #ifdef DRFLAC_64BIT
82466 #define drflac__be2host__cache_line drflac__be2host_64
82467 #else
82468 #define drflac__be2host__cache_line drflac__be2host_32
82469 #endif
82470 #define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
82471 #define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
82472 #define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
82473 #define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
82474 #define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
82475 #define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
82476 #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
82477 #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)))
82478 #define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
82479 #define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
82480 #define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
82481 #ifndef DR_FLAC_NO_CRC
82482 static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs)
82483 {
82484 bs->crc16 = 0;
82485 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
82486 }
82487 static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs)
82488 {
82489 if (bs->crc16CacheIgnoredBytes == 0) {
82490 bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache);
82491 } else {
82492 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
82493 bs->crc16CacheIgnoredBytes = 0;
82494 }
82495 }
82496 static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
82497 {
82498 DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
82499 if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
82500 drflac__update_crc16(bs);
82501 } else {
82502 bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
82503 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
82504 }
82505 return bs->crc16;
82506 }
82507 #endif
82508 static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
82509 {
82510 size_t bytesRead;
82511 size_t alignedL1LineCount;
82512 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
82513 bs->cache = bs->cacheL2[bs->nextL2Line++];
82514 return DRFLAC_TRUE;
82515 }
82516 if (bs->unalignedByteCount > 0) {
82517 return DRFLAC_FALSE;
82518 }
82519 bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
82520 bs->nextL2Line = 0;
82521 if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
82522 bs->cache = bs->cacheL2[bs->nextL2Line++];
82523 return DRFLAC_TRUE;
82524 }
82525 alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
82526 bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
82527 if (bs->unalignedByteCount > 0) {
82528 bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
82529 }
82530 if (alignedL1LineCount > 0) {
82531 size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
82532 size_t i;
82533 for (i = alignedL1LineCount; i > 0; --i) {
82534 bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
82535 }
82536 bs->nextL2Line = (drflac_uint32)offset;
82537 bs->cache = bs->cacheL2[bs->nextL2Line++];
82538 return DRFLAC_TRUE;
82539 } else {
82540 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
82541 return DRFLAC_FALSE;
82542 }
82543 }
82544 static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
82545 {
82546 size_t bytesRead;
82547 #ifndef DR_FLAC_NO_CRC
82548 drflac__update_crc16(bs);
82549 #endif
82550 if (drflac__reload_l1_cache_from_l2(bs)) {
82551 bs->cache = drflac__be2host__cache_line(bs->cache);
82552 bs->consumedBits = 0;
82553 #ifndef DR_FLAC_NO_CRC
82554 bs->crc16Cache = bs->cache;
82555 #endif
82556 return DRFLAC_TRUE;
82557 }
82558 bytesRead = bs->unalignedByteCount;
82559 if (bytesRead == 0) {
82560 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
82561 return DRFLAC_FALSE;
82562 }
82563 DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs));
82564 bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
82565 bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
82566 bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs));
82567 bs->unalignedByteCount = 0;
82568 #ifndef DR_FLAC_NO_CRC
82569 bs->crc16Cache = bs->cache >> bs->consumedBits;
82570 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
82571 #endif
82572 return DRFLAC_TRUE;
82573 }
82574 static void drflac__reset_cache(drflac_bs* bs)
82575 {
82576 bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
82577 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
82578 bs->cache = 0;
82579 bs->unalignedByteCount = 0;
82580 bs->unalignedCache = 0;
82581 #ifndef DR_FLAC_NO_CRC
82582 bs->crc16Cache = 0;
82583 bs->crc16CacheIgnoredBytes = 0;
82584 #endif
82585 }
82586 static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut)
82587 {
82588 DRFLAC_ASSERT(bs != NULL);
82589 DRFLAC_ASSERT(pResultOut != NULL);
82590 DRFLAC_ASSERT(bitCount > 0);
82591 DRFLAC_ASSERT(bitCount <= 32);
82592 if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
82593 if (!drflac__reload_cache(bs)) {
82594 return DRFLAC_FALSE;
82595 }
82596 }
82597 if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
82598 #ifdef DRFLAC_64BIT
82599 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
82600 bs->consumedBits += bitCount;
82601 bs->cache <<= bitCount;
82602 #else
82603 if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
82604 *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
82605 bs->consumedBits += bitCount;
82606 bs->cache <<= bitCount;
82607 } else {
82608 *pResultOut = (drflac_uint32)bs->cache;
82609 bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
82610 bs->cache = 0;
82611 }
82612 #endif
82613 return DRFLAC_TRUE;
82614 } else {
82615 drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
82616 drflac_uint32 bitCountLo = bitCount - bitCountHi;
82617 drflac_uint32 resultHi;
82618 DRFLAC_ASSERT(bitCountHi > 0);
82619 DRFLAC_ASSERT(bitCountHi < 32);
82620 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
82621 if (!drflac__reload_cache(bs)) {
82622 return DRFLAC_FALSE;
82623 }
82624 if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
82625 return DRFLAC_FALSE;
82626 }
82627 *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
82628 bs->consumedBits += bitCountLo;
82629 bs->cache <<= bitCountLo;
82630 return DRFLAC_TRUE;
82631 }
82632 }
82633 static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
82634 {
82635 drflac_uint32 result;
82636 DRFLAC_ASSERT(bs != NULL);
82637 DRFLAC_ASSERT(pResult != NULL);
82638 DRFLAC_ASSERT(bitCount > 0);
82639 DRFLAC_ASSERT(bitCount <= 32);
82640 if (!drflac__read_uint32(bs, bitCount, &result)) {
82641 return DRFLAC_FALSE;
82642 }
82643 if (bitCount < 32) {
82644 drflac_uint32 signbit;
82645 signbit = ((result >> (bitCount-1)) & 0x01);
82646 result |= (~signbit + 1) << bitCount;
82647 }
82648 *pResult = (drflac_int32)result;
82649 return DRFLAC_TRUE;
82650 }
82651 #ifdef DRFLAC_64BIT
82652 static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut)
82653 {
82654 drflac_uint32 resultHi;
82655 drflac_uint32 resultLo;
82656 DRFLAC_ASSERT(bitCount <= 64);
82657 DRFLAC_ASSERT(bitCount > 32);
82658 if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
82659 return DRFLAC_FALSE;
82660 }
82661 if (!drflac__read_uint32(bs, 32, &resultLo)) {
82662 return DRFLAC_FALSE;
82663 }
82664 *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo);
82665 return DRFLAC_TRUE;
82666 }
82667 #endif
82668 #if 0
82669 static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut)
82670 {
82671 drflac_uint64 result;
82672 drflac_uint64 signbit;
82673 DRFLAC_ASSERT(bitCount <= 64);
82674 if (!drflac__read_uint64(bs, bitCount, &result)) {
82675 return DRFLAC_FALSE;
82676 }
82677 signbit = ((result >> (bitCount-1)) & 0x01);
82678 result |= (~signbit + 1) << bitCount;
82679 *pResultOut = (drflac_int64)result;
82680 return DRFLAC_TRUE;
82681 }
82682 #endif
82683 static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult)
82684 {
82685 drflac_uint32 result;
82686 DRFLAC_ASSERT(bs != NULL);
82687 DRFLAC_ASSERT(pResult != NULL);
82688 DRFLAC_ASSERT(bitCount > 0);
82689 DRFLAC_ASSERT(bitCount <= 16);
82690 if (!drflac__read_uint32(bs, bitCount, &result)) {
82691 return DRFLAC_FALSE;
82692 }
82693 *pResult = (drflac_uint16)result;
82694 return DRFLAC_TRUE;
82695 }
82696 #if 0
82697 static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult)
82698 {
82699 drflac_int32 result;
82700 DRFLAC_ASSERT(bs != NULL);
82701 DRFLAC_ASSERT(pResult != NULL);
82702 DRFLAC_ASSERT(bitCount > 0);
82703 DRFLAC_ASSERT(bitCount <= 16);
82704 if (!drflac__read_int32(bs, bitCount, &result)) {
82705 return DRFLAC_FALSE;
82706 }
82707 *pResult = (drflac_int16)result;
82708 return DRFLAC_TRUE;
82709 }
82710 #endif
82711 static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult)
82712 {
82713 drflac_uint32 result;
82714 DRFLAC_ASSERT(bs != NULL);
82715 DRFLAC_ASSERT(pResult != NULL);
82716 DRFLAC_ASSERT(bitCount > 0);
82717 DRFLAC_ASSERT(bitCount <= 8);
82718 if (!drflac__read_uint32(bs, bitCount, &result)) {
82719 return DRFLAC_FALSE;
82720 }
82721 *pResult = (drflac_uint8)result;
82722 return DRFLAC_TRUE;
82723 }
82724 static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult)
82725 {
82726 drflac_int32 result;
82727 DRFLAC_ASSERT(bs != NULL);
82728 DRFLAC_ASSERT(pResult != NULL);
82729 DRFLAC_ASSERT(bitCount > 0);
82730 DRFLAC_ASSERT(bitCount <= 8);
82731 if (!drflac__read_int32(bs, bitCount, &result)) {
82732 return DRFLAC_FALSE;
82733 }
82734 *pResult = (drflac_int8)result;
82735 return DRFLAC_TRUE;
82736 }
82737 static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
82738 {
82739 if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
82740 bs->consumedBits += (drflac_uint32)bitsToSeek;
82741 bs->cache <<= bitsToSeek;
82742 return DRFLAC_TRUE;
82743 } else {
82744 bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
82745 bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
82746 bs->cache = 0;
82747 #ifdef DRFLAC_64BIT
82748 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
82749 drflac_uint64 bin;
82750 if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
82751 return DRFLAC_FALSE;
82752 }
82753 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
82754 }
82755 #else
82756 while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
82757 drflac_uint32 bin;
82758 if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
82759 return DRFLAC_FALSE;
82760 }
82761 bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
82762 }
82763 #endif
82764 while (bitsToSeek >= 8) {
82765 drflac_uint8 bin;
82766 if (!drflac__read_uint8(bs, 8, &bin)) {
82767 return DRFLAC_FALSE;
82768 }
82769 bitsToSeek -= 8;
82770 }
82771 if (bitsToSeek > 0) {
82772 drflac_uint8 bin;
82773 if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) {
82774 return DRFLAC_FALSE;
82775 }
82776 bitsToSeek = 0;
82777 }
82778 DRFLAC_ASSERT(bitsToSeek == 0);
82779 return DRFLAC_TRUE;
82780 }
82781 }
82782 static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
82783 {
82784 DRFLAC_ASSERT(bs != NULL);
82785 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
82786 return DRFLAC_FALSE;
82787 }
82788 for (;;) {
82789 drflac_uint8 hi;
82790 #ifndef DR_FLAC_NO_CRC
82791 drflac__reset_crc16(bs);
82792 #endif
82793 if (!drflac__read_uint8(bs, 8, &hi)) {
82794 return DRFLAC_FALSE;
82795 }
82796 if (hi == 0xFF) {
82797 drflac_uint8 lo;
82798 if (!drflac__read_uint8(bs, 6, &lo)) {
82799 return DRFLAC_FALSE;
82800 }
82801 if (lo == 0x3E) {
82802 return DRFLAC_TRUE;
82803 } else {
82804 if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
82805 return DRFLAC_FALSE;
82806 }
82807 }
82808 }
82809 }
82810 }
82811 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
82812 #define DRFLAC_IMPLEMENT_CLZ_LZCNT
82813 #endif
82814 #if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__)
82815 #define DRFLAC_IMPLEMENT_CLZ_MSVC
82816 #endif
82817 #if defined(__WATCOMC__) && defined(__386__)
82818 #define DRFLAC_IMPLEMENT_CLZ_WATCOM
82819 #endif
82820 #ifdef __MRC__
82821 #include <intrinsics.h>
82822 #define DRFLAC_IMPLEMENT_CLZ_MRC
82823 #endif
82824 static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
82825 {
82826 drflac_uint32 n;
82827 static drflac_uint32 clz_table_4[] = {
82828 0,
82829 4,
82830 3, 3,
82831 2, 2, 2, 2,
82832 1, 1, 1, 1, 1, 1, 1, 1
82833 };
82834 if (x == 0) {
82835 return sizeof(x)*8;
82836 }
82837 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
82838 if (n == 0) {
82839 #ifdef DRFLAC_64BIT
82840 if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
82841 if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
82842 if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
82843 if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
82844 #else
82845 if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
82846 if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
82847 if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
82848 #endif
82849 n += clz_table_4[x >> (sizeof(x)*8 - 4)];
82850 }
82851 return n - 1;
82852 }
82853 #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
82854 static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void)
82855 {
82856 #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
82857 return DRFLAC_TRUE;
82858 #elif defined(__MRC__)
82859 return DRFLAC_TRUE;
82860 #else
82861 #ifdef DRFLAC_HAS_LZCNT_INTRINSIC
82862 return drflac__gIsLZCNTSupported;
82863 #else
82864 return DRFLAC_FALSE;
82865 #endif
82866 #endif
82867 }
82868 static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
82869 {
82870 #if defined(_MSC_VER)
82871 #ifdef DRFLAC_64BIT
82872 return (drflac_uint32)__lzcnt64(x);
82873 #else
82874 return (drflac_uint32)__lzcnt(x);
82875 #endif
82876 #else
82877 #if defined(__GNUC__) || defined(__clang__)
82878 #if defined(DRFLAC_X64)
82879 {
82880 drflac_uint64 r;
82881 __asm__ __volatile__ (
82882 "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
82883 );
82884 return (drflac_uint32)r;
82885 }
82886 #elif defined(DRFLAC_X86)
82887 {
82888 drflac_uint32 r;
82889 __asm__ __volatile__ (
82890 "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
82891 );
82892 return r;
82893 }
82894 #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT)
82895 {
82896 unsigned int r;
82897 __asm__ __volatile__ (
82898 #if defined(DRFLAC_64BIT)
82899 "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x)
82900 #else
82901 "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
82902 #endif
82903 );
82904 return r;
82905 }
82906 #else
82907 if (x == 0) {
82908 return sizeof(x)*8;
82909 }
82910 #ifdef DRFLAC_64BIT
82911 return (drflac_uint32)__builtin_clzll((drflac_uint64)x);
82912 #else
82913 return (drflac_uint32)__builtin_clzl((drflac_uint32)x);
82914 #endif
82915 #endif
82916 #else
82917 #error "This compiler does not support the lzcnt intrinsic."
82918 #endif
82919 #endif
82920 }
82921 #endif
82922 #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
82923 #include <intrin.h>
82924 static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
82925 {
82926 drflac_uint32 n;
82927 if (x == 0) {
82928 return sizeof(x)*8;
82929 }
82930 #ifdef DRFLAC_64BIT
82931 _BitScanReverse64((unsigned long*)&n, x);
82932 #else
82933 _BitScanReverse((unsigned long*)&n, x);
82934 #endif
82935 return sizeof(x)*8 - n - 1;
82936 }
82937 #endif
82938 #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM
82939 static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32);
82940 #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT
82941 #pragma aux drflac__clz_watcom_lzcnt = \
82942 "db 0F3h, 0Fh, 0BDh, 0C0h" \
82943 parm [eax] \
82944 value [eax] \
82945 modify nomemory;
82946 #else
82947 #pragma aux drflac__clz_watcom = \
82948 "bsr eax, eax" \
82949 "xor eax, 31" \
82950 parm [eax] nomemory \
82951 value [eax] \
82952 modify exact [eax] nomemory;
82953 #endif
82954 #endif
82955 static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x)
82956 {
82957 #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
82958 if (drflac__is_lzcnt_supported()) {
82959 return drflac__clz_lzcnt(x);
82960 } else
82961 #endif
82962 {
82963 #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
82964 return drflac__clz_msvc(x);
82965 #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT)
82966 return drflac__clz_watcom_lzcnt(x);
82967 #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM)
82968 return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x);
82969 #elif defined(__MRC__)
82970 return __cntlzw(x);
82971 #else
82972 return drflac__clz_software(x);
82973 #endif
82974 }
82975 }
82976 static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
82977 {
82978 drflac_uint32 zeroCounter = 0;
82979 drflac_uint32 setBitOffsetPlus1;
82980 while (bs->cache == 0) {
82981 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
82982 if (!drflac__reload_cache(bs)) {
82983 return DRFLAC_FALSE;
82984 }
82985 }
82986 if (bs->cache == 1) {
82987 *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1;
82988 if (!drflac__reload_cache(bs)) {
82989 return DRFLAC_FALSE;
82990 }
82991 return DRFLAC_TRUE;
82992 }
82993 setBitOffsetPlus1 = drflac__clz(bs->cache);
82994 setBitOffsetPlus1 += 1;
82995 if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
82996 return DRFLAC_FALSE;
82997 }
82998 bs->consumedBits += setBitOffsetPlus1;
82999 bs->cache <<= setBitOffsetPlus1;
83000 *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
83001 return DRFLAC_TRUE;
83002 }
83003 static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart)
83004 {
83005 DRFLAC_ASSERT(bs != NULL);
83006 DRFLAC_ASSERT(offsetFromStart > 0);
83007 if (offsetFromStart > 0x7FFFFFFF) {
83008 drflac_uint64 bytesRemaining = offsetFromStart;
83009 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
83010 return DRFLAC_FALSE;
83011 }
83012 bytesRemaining -= 0x7FFFFFFF;
83013 while (bytesRemaining > 0x7FFFFFFF) {
83014 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
83015 return DRFLAC_FALSE;
83016 }
83017 bytesRemaining -= 0x7FFFFFFF;
83018 }
83019 if (bytesRemaining > 0) {
83020 if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
83021 return DRFLAC_FALSE;
83022 }
83023 }
83024 } else {
83025 if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
83026 return DRFLAC_FALSE;
83027 }
83028 }
83029 drflac__reset_cache(bs);
83030 return DRFLAC_TRUE;
83031 }
83032 static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut)
83033 {
83034 drflac_uint8 crc;
83035 drflac_uint64 result;
83036 drflac_uint8 utf8[7] = {0};
83037 int byteCount;
83038 int i;
83039 DRFLAC_ASSERT(bs != NULL);
83040 DRFLAC_ASSERT(pNumberOut != NULL);
83041 DRFLAC_ASSERT(pCRCOut != NULL);
83042 crc = *pCRCOut;
83043 if (!drflac__read_uint8(bs, 8, utf8)) {
83044 *pNumberOut = 0;
83045 return DRFLAC_AT_END;
83046 }
83047 crc = drflac_crc8(crc, utf8[0], 8);
83048 if ((utf8[0] & 0x80) == 0) {
83049 *pNumberOut = utf8[0];
83050 *pCRCOut = crc;
83051 return DRFLAC_SUCCESS;
83052 }
83053 if ((utf8[0] & 0xE0) == 0xC0) {
83054 byteCount = 2;
83055 } else if ((utf8[0] & 0xF0) == 0xE0) {
83056 byteCount = 3;
83057 } else if ((utf8[0] & 0xF8) == 0xF0) {
83058 byteCount = 4;
83059 } else if ((utf8[0] & 0xFC) == 0xF8) {
83060 byteCount = 5;
83061 } else if ((utf8[0] & 0xFE) == 0xFC) {
83062 byteCount = 6;
83063 } else if ((utf8[0] & 0xFF) == 0xFE) {
83064 byteCount = 7;
83065 } else {
83066 *pNumberOut = 0;
83067 return DRFLAC_CRC_MISMATCH;
83068 }
83069 DRFLAC_ASSERT(byteCount > 1);
83070 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
83071 for (i = 1; i < byteCount; ++i) {
83072 if (!drflac__read_uint8(bs, 8, utf8 + i)) {
83073 *pNumberOut = 0;
83074 return DRFLAC_AT_END;
83075 }
83076 crc = drflac_crc8(crc, utf8[i], 8);
83077 result = (result << 6) | (utf8[i] & 0x3F);
83078 }
83079 *pNumberOut = result;
83080 *pCRCOut = crc;
83081 return DRFLAC_SUCCESS;
83082 }
83083 static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x)
83084 {
83085 #if 1
83086 drflac_uint32 result = 0;
83087 while (x > 0) {
83088 result += 1;
83089 x >>= 1;
83090 }
83091 return result;
83092 #endif
83093 }
83094 static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision)
83095 {
83096 return bitsPerSample + precision + drflac__ilog2_u32(order) > 32;
83097 }
83098 #if defined(__clang__)
83099 __attribute__((no_sanitize("signed-integer-overflow")))
83100 #endif
83101 static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
83102 {
83103 drflac_int32 prediction = 0;
83104 DRFLAC_ASSERT(order <= 32);
83105 switch (order)
83106 {
83107 case 32: prediction += coefficients[31] * pDecodedSamples[-32];
83108 case 31: prediction += coefficients[30] * pDecodedSamples[-31];
83109 case 30: prediction += coefficients[29] * pDecodedSamples[-30];
83110 case 29: prediction += coefficients[28] * pDecodedSamples[-29];
83111 case 28: prediction += coefficients[27] * pDecodedSamples[-28];
83112 case 27: prediction += coefficients[26] * pDecodedSamples[-27];
83113 case 26: prediction += coefficients[25] * pDecodedSamples[-26];
83114 case 25: prediction += coefficients[24] * pDecodedSamples[-25];
83115 case 24: prediction += coefficients[23] * pDecodedSamples[-24];
83116 case 23: prediction += coefficients[22] * pDecodedSamples[-23];
83117 case 22: prediction += coefficients[21] * pDecodedSamples[-22];
83118 case 21: prediction += coefficients[20] * pDecodedSamples[-21];
83119 case 20: prediction += coefficients[19] * pDecodedSamples[-20];
83120 case 19: prediction += coefficients[18] * pDecodedSamples[-19];
83121 case 18: prediction += coefficients[17] * pDecodedSamples[-18];
83122 case 17: prediction += coefficients[16] * pDecodedSamples[-17];
83123 case 16: prediction += coefficients[15] * pDecodedSamples[-16];
83124 case 15: prediction += coefficients[14] * pDecodedSamples[-15];
83125 case 14: prediction += coefficients[13] * pDecodedSamples[-14];
83126 case 13: prediction += coefficients[12] * pDecodedSamples[-13];
83127 case 12: prediction += coefficients[11] * pDecodedSamples[-12];
83128 case 11: prediction += coefficients[10] * pDecodedSamples[-11];
83129 case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
83130 case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
83131 case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
83132 case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
83133 case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
83134 case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
83135 case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
83136 case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
83137 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
83138 case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
83139 }
83140 return (drflac_int32)(prediction >> shift);
83141 }
83142 static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
83143 {
83144 drflac_int64 prediction;
83145 DRFLAC_ASSERT(order <= 32);
83146 #ifndef DRFLAC_64BIT
83147 if (order == 8)
83148 {
83149 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83150 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83151 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83152 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83153 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83154 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
83155 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
83156 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
83157 }
83158 else if (order == 7)
83159 {
83160 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83161 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83162 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83163 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83164 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83165 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
83166 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
83167 }
83168 else if (order == 3)
83169 {
83170 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83171 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83172 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83173 }
83174 else if (order == 6)
83175 {
83176 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83177 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83178 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83179 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83180 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83181 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
83182 }
83183 else if (order == 5)
83184 {
83185 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83186 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83187 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83188 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83189 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83190 }
83191 else if (order == 4)
83192 {
83193 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83194 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83195 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83196 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83197 }
83198 else if (order == 12)
83199 {
83200 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83201 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83202 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83203 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83204 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83205 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
83206 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
83207 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
83208 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
83209 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
83210 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
83211 prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
83212 }
83213 else if (order == 2)
83214 {
83215 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83216 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83217 }
83218 else if (order == 1)
83219 {
83220 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83221 }
83222 else if (order == 10)
83223 {
83224 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83225 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83226 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83227 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83228 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83229 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
83230 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
83231 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
83232 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
83233 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
83234 }
83235 else if (order == 9)
83236 {
83237 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83238 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83239 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83240 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83241 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83242 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
83243 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
83244 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
83245 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
83246 }
83247 else if (order == 11)
83248 {
83249 prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
83250 prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
83251 prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
83252 prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
83253 prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
83254 prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
83255 prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
83256 prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
83257 prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
83258 prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
83259 prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
83260 }
83261 else
83262 {
83263 int j;
83264 prediction = 0;
83265 for (j = 0; j < (int)order; ++j) {
83266 prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1];
83267 }
83268 }
83269 #endif
83270 #ifdef DRFLAC_64BIT
83271 prediction = 0;
83272 switch (order)
83273 {
83274 case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
83275 case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
83276 case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
83277 case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
83278 case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
83279 case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
83280 case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
83281 case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
83282 case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
83283 case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
83284 case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
83285 case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
83286 case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
83287 case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
83288 case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
83289 case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
83290 case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
83291 case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
83292 case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
83293 case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
83294 case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
83295 case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
83296 case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10];
83297 case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9];
83298 case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8];
83299 case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7];
83300 case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6];
83301 case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5];
83302 case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4];
83303 case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
83304 case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
83305 case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
83306 }
83307 #endif
83308 return (drflac_int32)(prediction >> shift);
83309 }
83310 #if 0
83311 static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
83312 {
83313 drflac_uint32 i;
83314 DRFLAC_ASSERT(bs != NULL);
83315 DRFLAC_ASSERT(pSamplesOut != NULL);
83316 for (i = 0; i < count; ++i) {
83317 drflac_uint32 zeroCounter = 0;
83318 for (;;) {
83319 drflac_uint8 bit;
83320 if (!drflac__read_uint8(bs, 1, &bit)) {
83321 return DRFLAC_FALSE;
83322 }
83323 if (bit == 0) {
83324 zeroCounter += 1;
83325 } else {
83326 break;
83327 }
83328 }
83329 drflac_uint32 decodedRice;
83330 if (riceParam > 0) {
83331 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
83332 return DRFLAC_FALSE;
83333 }
83334 } else {
83335 decodedRice = 0;
83336 }
83337 decodedRice |= (zeroCounter << riceParam);
83338 if ((decodedRice & 0x01)) {
83339 decodedRice = ~(decodedRice >> 1);
83340 } else {
83341 decodedRice = (decodedRice >> 1);
83342 }
83343 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
83344 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
83345 } else {
83346 pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
83347 }
83348 }
83349 return DRFLAC_TRUE;
83350 }
83351 #endif
83352 #if 0
83353 static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
83354 {
83355 drflac_uint32 zeroCounter = 0;
83356 drflac_uint32 decodedRice;
83357 for (;;) {
83358 drflac_uint8 bit;
83359 if (!drflac__read_uint8(bs, 1, &bit)) {
83360 return DRFLAC_FALSE;
83361 }
83362 if (bit == 0) {
83363 zeroCounter += 1;
83364 } else {
83365 break;
83366 }
83367 }
83368 if (riceParam > 0) {
83369 if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
83370 return DRFLAC_FALSE;
83371 }
83372 } else {
83373 decodedRice = 0;
83374 }
83375 *pZeroCounterOut = zeroCounter;
83376 *pRiceParamPartOut = decodedRice;
83377 return DRFLAC_TRUE;
83378 }
83379 #endif
83380 #if 0
83381 static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
83382 {
83383 drflac_cache_t riceParamMask;
83384 drflac_uint32 zeroCounter;
83385 drflac_uint32 setBitOffsetPlus1;
83386 drflac_uint32 riceParamPart;
83387 drflac_uint32 riceLength;
83388 DRFLAC_ASSERT(riceParam > 0);
83389 riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
83390 zeroCounter = 0;
83391 while (bs->cache == 0) {
83392 zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
83393 if (!drflac__reload_cache(bs)) {
83394 return DRFLAC_FALSE;
83395 }
83396 }
83397 setBitOffsetPlus1 = drflac__clz(bs->cache);
83398 zeroCounter += setBitOffsetPlus1;
83399 setBitOffsetPlus1 += 1;
83400 riceLength = setBitOffsetPlus1 + riceParam;
83401 if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
83402 riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
83403 bs->consumedBits += riceLength;
83404 bs->cache <<= riceLength;
83405 } else {
83406 drflac_uint32 bitCountLo;
83407 drflac_cache_t resultHi;
83408 bs->consumedBits += riceLength;
83409 bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1);
83410 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
83411 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);
83412 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
83413 #ifndef DR_FLAC_NO_CRC
83414 drflac__update_crc16(bs);
83415 #endif
83416 bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83417 bs->consumedBits = 0;
83418 #ifndef DR_FLAC_NO_CRC
83419 bs->crc16Cache = bs->cache;
83420 #endif
83421 } else {
83422 if (!drflac__reload_cache(bs)) {
83423 return DRFLAC_FALSE;
83424 }
83425 if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
83426 return DRFLAC_FALSE;
83427 }
83428 }
83429 riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
83430 bs->consumedBits += bitCountLo;
83431 bs->cache <<= bitCountLo;
83432 }
83433 pZeroCounterOut[0] = zeroCounter;
83434 pRiceParamPartOut[0] = riceParamPart;
83435 return DRFLAC_TRUE;
83436 }
83437 #endif
83438 static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
83439 {
83440 drflac_uint32 riceParamPlus1 = riceParam + 1;
83441 drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
83442 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
83443 drflac_cache_t bs_cache = bs->cache;
83444 drflac_uint32 bs_consumedBits = bs->consumedBits;
83445 drflac_uint32 lzcount = drflac__clz(bs_cache);
83446 if (lzcount < sizeof(bs_cache)*8) {
83447 pZeroCounterOut[0] = lzcount;
83448 extract_rice_param_part:
83449 bs_cache <<= lzcount;
83450 bs_consumedBits += lzcount;
83451 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
83452 pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
83453 bs_cache <<= riceParamPlus1;
83454 bs_consumedBits += riceParamPlus1;
83455 } else {
83456 drflac_uint32 riceParamPartHi;
83457 drflac_uint32 riceParamPartLo;
83458 drflac_uint32 riceParamPartLoBitCount;
83459 riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
83460 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
83461 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
83462 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
83463 #ifndef DR_FLAC_NO_CRC
83464 drflac__update_crc16(bs);
83465 #endif
83466 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83467 bs_consumedBits = riceParamPartLoBitCount;
83468 #ifndef DR_FLAC_NO_CRC
83469 bs->crc16Cache = bs_cache;
83470 #endif
83471 } else {
83472 if (!drflac__reload_cache(bs)) {
83473 return DRFLAC_FALSE;
83474 }
83475 if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
83476 return DRFLAC_FALSE;
83477 }
83478 bs_cache = bs->cache;
83479 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
83480 }
83481 riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
83482 pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
83483 bs_cache <<= riceParamPartLoBitCount;
83484 }
83485 } else {
83486 drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
83487 for (;;) {
83488 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
83489 #ifndef DR_FLAC_NO_CRC
83490 drflac__update_crc16(bs);
83491 #endif
83492 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83493 bs_consumedBits = 0;
83494 #ifndef DR_FLAC_NO_CRC
83495 bs->crc16Cache = bs_cache;
83496 #endif
83497 } else {
83498 if (!drflac__reload_cache(bs)) {
83499 return DRFLAC_FALSE;
83500 }
83501 bs_cache = bs->cache;
83502 bs_consumedBits = bs->consumedBits;
83503 }
83504 lzcount = drflac__clz(bs_cache);
83505 zeroCounter += lzcount;
83506 if (lzcount < sizeof(bs_cache)*8) {
83507 break;
83508 }
83509 }
83510 pZeroCounterOut[0] = zeroCounter;
83511 goto extract_rice_param_part;
83512 }
83513 bs->cache = bs_cache;
83514 bs->consumedBits = bs_consumedBits;
83515 return DRFLAC_TRUE;
83516 }
83517 static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam)
83518 {
83519 drflac_uint32 riceParamPlus1 = riceParam + 1;
83520 drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
83521 drflac_cache_t bs_cache = bs->cache;
83522 drflac_uint32 bs_consumedBits = bs->consumedBits;
83523 drflac_uint32 lzcount = drflac__clz(bs_cache);
83524 if (lzcount < sizeof(bs_cache)*8) {
83525 extract_rice_param_part:
83526 bs_cache <<= lzcount;
83527 bs_consumedBits += lzcount;
83528 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
83529 bs_cache <<= riceParamPlus1;
83530 bs_consumedBits += riceParamPlus1;
83531 } else {
83532 drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
83533 DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
83534 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
83535 #ifndef DR_FLAC_NO_CRC
83536 drflac__update_crc16(bs);
83537 #endif
83538 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83539 bs_consumedBits = riceParamPartLoBitCount;
83540 #ifndef DR_FLAC_NO_CRC
83541 bs->crc16Cache = bs_cache;
83542 #endif
83543 } else {
83544 if (!drflac__reload_cache(bs)) {
83545 return DRFLAC_FALSE;
83546 }
83547 if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
83548 return DRFLAC_FALSE;
83549 }
83550 bs_cache = bs->cache;
83551 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
83552 }
83553 bs_cache <<= riceParamPartLoBitCount;
83554 }
83555 } else {
83556 for (;;) {
83557 if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
83558 #ifndef DR_FLAC_NO_CRC
83559 drflac__update_crc16(bs);
83560 #endif
83561 bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
83562 bs_consumedBits = 0;
83563 #ifndef DR_FLAC_NO_CRC
83564 bs->crc16Cache = bs_cache;
83565 #endif
83566 } else {
83567 if (!drflac__reload_cache(bs)) {
83568 return DRFLAC_FALSE;
83569 }
83570 bs_cache = bs->cache;
83571 bs_consumedBits = bs->consumedBits;
83572 }
83573 lzcount = drflac__clz(bs_cache);
83574 if (lzcount < sizeof(bs_cache)*8) {
83575 break;
83576 }
83577 }
83578 goto extract_rice_param_part;
83579 }
83580 bs->cache = bs_cache;
83581 bs->consumedBits = bs_consumedBits;
83582 return DRFLAC_TRUE;
83583 }
83584 static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
83585 {
83586 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
83587 drflac_uint32 zeroCountPart0;
83588 drflac_uint32 riceParamPart0;
83589 drflac_uint32 riceParamMask;
83590 drflac_uint32 i;
83591 DRFLAC_ASSERT(bs != NULL);
83592 DRFLAC_ASSERT(pSamplesOut != NULL);
83593 (void)bitsPerSample;
83594 (void)order;
83595 (void)shift;
83596 (void)coefficients;
83597 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
83598 i = 0;
83599 while (i < count) {
83600 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
83601 return DRFLAC_FALSE;
83602 }
83603 riceParamPart0 &= riceParamMask;
83604 riceParamPart0 |= (zeroCountPart0 << riceParam);
83605 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83606 pSamplesOut[i] = riceParamPart0;
83607 i += 1;
83608 }
83609 return DRFLAC_TRUE;
83610 }
83611 static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
83612 {
83613 drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
83614 drflac_uint32 zeroCountPart0 = 0;
83615 drflac_uint32 zeroCountPart1 = 0;
83616 drflac_uint32 zeroCountPart2 = 0;
83617 drflac_uint32 zeroCountPart3 = 0;
83618 drflac_uint32 riceParamPart0 = 0;
83619 drflac_uint32 riceParamPart1 = 0;
83620 drflac_uint32 riceParamPart2 = 0;
83621 drflac_uint32 riceParamPart3 = 0;
83622 drflac_uint32 riceParamMask;
83623 const drflac_int32* pSamplesOutEnd;
83624 drflac_uint32 i;
83625 DRFLAC_ASSERT(bs != NULL);
83626 DRFLAC_ASSERT(pSamplesOut != NULL);
83627 if (lpcOrder == 0) {
83628 return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
83629 }
83630 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
83631 pSamplesOutEnd = pSamplesOut + (count & ~3);
83632 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
83633 while (pSamplesOut < pSamplesOutEnd) {
83634 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
83635 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
83636 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
83637 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
83638 return DRFLAC_FALSE;
83639 }
83640 riceParamPart0 &= riceParamMask;
83641 riceParamPart1 &= riceParamMask;
83642 riceParamPart2 &= riceParamMask;
83643 riceParamPart3 &= riceParamMask;
83644 riceParamPart0 |= (zeroCountPart0 << riceParam);
83645 riceParamPart1 |= (zeroCountPart1 << riceParam);
83646 riceParamPart2 |= (zeroCountPart2 << riceParam);
83647 riceParamPart3 |= (zeroCountPart3 << riceParam);
83648 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83649 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
83650 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
83651 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
83652 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83653 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
83654 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
83655 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
83656 pSamplesOut += 4;
83657 }
83658 } else {
83659 while (pSamplesOut < pSamplesOutEnd) {
83660 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
83661 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
83662 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
83663 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
83664 return DRFLAC_FALSE;
83665 }
83666 riceParamPart0 &= riceParamMask;
83667 riceParamPart1 &= riceParamMask;
83668 riceParamPart2 &= riceParamMask;
83669 riceParamPart3 &= riceParamMask;
83670 riceParamPart0 |= (zeroCountPart0 << riceParam);
83671 riceParamPart1 |= (zeroCountPart1 << riceParam);
83672 riceParamPart2 |= (zeroCountPart2 << riceParam);
83673 riceParamPart3 |= (zeroCountPart3 << riceParam);
83674 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83675 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
83676 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
83677 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
83678 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83679 pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
83680 pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
83681 pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
83682 pSamplesOut += 4;
83683 }
83684 }
83685 i = (count & ~3);
83686 while (i < count) {
83687 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
83688 return DRFLAC_FALSE;
83689 }
83690 riceParamPart0 &= riceParamMask;
83691 riceParamPart0 |= (zeroCountPart0 << riceParam);
83692 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
83693 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
83694 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83695 } else {
83696 pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
83697 }
83698 i += 1;
83699 pSamplesOut += 1;
83700 }
83701 return DRFLAC_TRUE;
83702 }
83703 #if defined(DRFLAC_SUPPORT_SSE2)
83704 static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
83705 {
83706 __m128i r;
83707 r = _mm_packs_epi32(a, b);
83708 r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
83709 r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
83710 r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
83711 return r;
83712 }
83713 #endif
83714 #if defined(DRFLAC_SUPPORT_SSE41)
83715 static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a)
83716 {
83717 return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
83718 }
83719 static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x)
83720 {
83721 __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
83722 __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
83723 return _mm_add_epi32(x64, x32);
83724 }
83725 static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x)
83726 {
83727 return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
83728 }
83729 static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count)
83730 {
83731 __m128i lo = _mm_srli_epi64(x, count);
83732 __m128i hi = _mm_srai_epi32(x, count);
83733 hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));
83734 return _mm_or_si128(lo, hi);
83735 }
83736 static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
83737 {
83738 int i;
83739 drflac_uint32 riceParamMask;
83740 drflac_int32* pDecodedSamples = pSamplesOut;
83741 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
83742 drflac_uint32 zeroCountParts0 = 0;
83743 drflac_uint32 zeroCountParts1 = 0;
83744 drflac_uint32 zeroCountParts2 = 0;
83745 drflac_uint32 zeroCountParts3 = 0;
83746 drflac_uint32 riceParamParts0 = 0;
83747 drflac_uint32 riceParamParts1 = 0;
83748 drflac_uint32 riceParamParts2 = 0;
83749 drflac_uint32 riceParamParts3 = 0;
83750 __m128i coefficients128_0;
83751 __m128i coefficients128_4;
83752 __m128i coefficients128_8;
83753 __m128i samples128_0;
83754 __m128i samples128_4;
83755 __m128i samples128_8;
83756 __m128i riceParamMask128;
83757 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
83758 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
83759 riceParamMask128 = _mm_set1_epi32(riceParamMask);
83760 coefficients128_0 = _mm_setzero_si128();
83761 coefficients128_4 = _mm_setzero_si128();
83762 coefficients128_8 = _mm_setzero_si128();
83763 samples128_0 = _mm_setzero_si128();
83764 samples128_4 = _mm_setzero_si128();
83765 samples128_8 = _mm_setzero_si128();
83766 #if 1
83767 {
83768 int runningOrder = order;
83769 if (runningOrder >= 4) {
83770 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
83771 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
83772 runningOrder -= 4;
83773 } else {
83774 switch (runningOrder) {
83775 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;
83776 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;
83777 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
83778 }
83779 runningOrder = 0;
83780 }
83781 if (runningOrder >= 4) {
83782 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
83783 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
83784 runningOrder -= 4;
83785 } else {
83786 switch (runningOrder) {
83787 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;
83788 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;
83789 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
83790 }
83791 runningOrder = 0;
83792 }
83793 if (runningOrder == 4) {
83794 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
83795 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
83796 runningOrder -= 4;
83797 } else {
83798 switch (runningOrder) {
83799 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;
83800 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;
83801 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
83802 }
83803 runningOrder = 0;
83804 }
83805 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
83806 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
83807 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
83808 }
83809 #else
83810 switch (order)
83811 {
83812 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
83813 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
83814 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
83815 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
83816 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
83817 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
83818 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
83819 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
83820 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
83821 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
83822 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
83823 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
83824 }
83825 #endif
83826 while (pDecodedSamples < pDecodedSamplesEnd) {
83827 __m128i prediction128;
83828 __m128i zeroCountPart128;
83829 __m128i riceParamPart128;
83830 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
83831 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
83832 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
83833 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
83834 return DRFLAC_FALSE;
83835 }
83836 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
83837 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
83838 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
83839 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
83840 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01)));
83841 if (order <= 4) {
83842 for (i = 0; i < 4; i += 1) {
83843 prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
83844 prediction128 = drflac__mm_hadd_epi32(prediction128);
83845 prediction128 = _mm_srai_epi32(prediction128, shift);
83846 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
83847 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
83848 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
83849 }
83850 } else if (order <= 8) {
83851 for (i = 0; i < 4; i += 1) {
83852 prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
83853 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
83854 prediction128 = drflac__mm_hadd_epi32(prediction128);
83855 prediction128 = _mm_srai_epi32(prediction128, shift);
83856 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
83857 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
83858 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
83859 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
83860 }
83861 } else {
83862 for (i = 0; i < 4; i += 1) {
83863 prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
83864 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));
83865 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
83866 prediction128 = drflac__mm_hadd_epi32(prediction128);
83867 prediction128 = _mm_srai_epi32(prediction128, shift);
83868 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
83869 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
83870 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
83871 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
83872 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
83873 }
83874 }
83875 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
83876 pDecodedSamples += 4;
83877 }
83878 i = (count & ~3);
83879 while (i < (int)count) {
83880 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
83881 return DRFLAC_FALSE;
83882 }
83883 riceParamParts0 &= riceParamMask;
83884 riceParamParts0 |= (zeroCountParts0 << riceParam);
83885 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
83886 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
83887 i += 1;
83888 pDecodedSamples += 1;
83889 }
83890 return DRFLAC_TRUE;
83891 }
83892 static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
83893 {
83894 int i;
83895 drflac_uint32 riceParamMask;
83896 drflac_int32* pDecodedSamples = pSamplesOut;
83897 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
83898 drflac_uint32 zeroCountParts0 = 0;
83899 drflac_uint32 zeroCountParts1 = 0;
83900 drflac_uint32 zeroCountParts2 = 0;
83901 drflac_uint32 zeroCountParts3 = 0;
83902 drflac_uint32 riceParamParts0 = 0;
83903 drflac_uint32 riceParamParts1 = 0;
83904 drflac_uint32 riceParamParts2 = 0;
83905 drflac_uint32 riceParamParts3 = 0;
83906 __m128i coefficients128_0;
83907 __m128i coefficients128_4;
83908 __m128i coefficients128_8;
83909 __m128i samples128_0;
83910 __m128i samples128_4;
83911 __m128i samples128_8;
83912 __m128i prediction128;
83913 __m128i riceParamMask128;
83914 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
83915 DRFLAC_ASSERT(order <= 12);
83916 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
83917 riceParamMask128 = _mm_set1_epi32(riceParamMask);
83918 prediction128 = _mm_setzero_si128();
83919 coefficients128_0 = _mm_setzero_si128();
83920 coefficients128_4 = _mm_setzero_si128();
83921 coefficients128_8 = _mm_setzero_si128();
83922 samples128_0 = _mm_setzero_si128();
83923 samples128_4 = _mm_setzero_si128();
83924 samples128_8 = _mm_setzero_si128();
83925 #if 1
83926 {
83927 int runningOrder = order;
83928 if (runningOrder >= 4) {
83929 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
83930 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
83931 runningOrder -= 4;
83932 } else {
83933 switch (runningOrder) {
83934 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;
83935 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;
83936 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
83937 }
83938 runningOrder = 0;
83939 }
83940 if (runningOrder >= 4) {
83941 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
83942 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
83943 runningOrder -= 4;
83944 } else {
83945 switch (runningOrder) {
83946 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;
83947 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;
83948 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
83949 }
83950 runningOrder = 0;
83951 }
83952 if (runningOrder == 4) {
83953 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
83954 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
83955 runningOrder -= 4;
83956 } else {
83957 switch (runningOrder) {
83958 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;
83959 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;
83960 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
83961 }
83962 runningOrder = 0;
83963 }
83964 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
83965 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
83966 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
83967 }
83968 #else
83969 switch (order)
83970 {
83971 case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
83972 case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
83973 case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
83974 case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
83975 case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
83976 case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
83977 case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
83978 case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
83979 case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
83980 case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
83981 case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
83982 case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
83983 }
83984 #endif
83985 while (pDecodedSamples < pDecodedSamplesEnd) {
83986 __m128i zeroCountPart128;
83987 __m128i riceParamPart128;
83988 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
83989 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
83990 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
83991 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
83992 return DRFLAC_FALSE;
83993 }
83994 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
83995 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
83996 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
83997 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
83998 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1)));
83999 for (i = 0; i < 4; i += 1) {
84000 prediction128 = _mm_xor_si128(prediction128, prediction128);
84001 switch (order)
84002 {
84003 case 12:
84004 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))));
84005 case 10:
84006 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))));
84007 case 8:
84008 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))));
84009 case 6:
84010 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))));
84011 case 4:
84012 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))));
84013 case 2:
84014 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))));
84015 }
84016 prediction128 = drflac__mm_hadd_epi64(prediction128);
84017 prediction128 = drflac__mm_srai_epi64(prediction128, shift);
84018 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
84019 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
84020 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
84021 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
84022 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
84023 }
84024 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
84025 pDecodedSamples += 4;
84026 }
84027 i = (count & ~3);
84028 while (i < (int)count) {
84029 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
84030 return DRFLAC_FALSE;
84031 }
84032 riceParamParts0 &= riceParamMask;
84033 riceParamParts0 |= (zeroCountParts0 << riceParam);
84034 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
84035 pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
84036 i += 1;
84037 pDecodedSamples += 1;
84038 }
84039 return DRFLAC_TRUE;
84040 }
84041 static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
84042 {
84043 DRFLAC_ASSERT(bs != NULL);
84044 DRFLAC_ASSERT(pSamplesOut != NULL);
84045 if (lpcOrder > 0 && lpcOrder <= 12) {
84046 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
84047 return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84048 } else {
84049 return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84050 }
84051 } else {
84052 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84053 }
84054 }
84055 #endif
84056 #if defined(DRFLAC_SUPPORT_NEON)
84057 static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x)
84058 {
84059 vst1q_s32(p+0, x.val[0]);
84060 vst1q_s32(p+4, x.val[1]);
84061 }
84062 static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x)
84063 {
84064 vst1q_u32(p+0, x.val[0]);
84065 vst1q_u32(p+4, x.val[1]);
84066 }
84067 static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x)
84068 {
84069 vst1q_f32(p+0, x.val[0]);
84070 vst1q_f32(p+4, x.val[1]);
84071 }
84072 static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x)
84073 {
84074 vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
84075 }
84076 static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x)
84077 {
84078 vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));
84079 }
84080 static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0)
84081 {
84082 drflac_int32 x[4];
84083 x[3] = x3;
84084 x[2] = x2;
84085 x[1] = x1;
84086 x[0] = x0;
84087 return vld1q_s32(x);
84088 }
84089 static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b)
84090 {
84091 return vextq_s32(b, a, 1);
84092 }
84093 static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
84094 {
84095 return vextq_u32(b, a, 1);
84096 }
84097 static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x)
84098 {
84099 int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
84100 return vpadd_s32(r, r);
84101 }
84102 static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x)
84103 {
84104 return vadd_s64(vget_high_s64(x), vget_low_s64(x));
84105 }
84106 static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x)
84107 {
84108 return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
84109 }
84110 static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x)
84111 {
84112 return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
84113 }
84114 static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x)
84115 {
84116 return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
84117 }
84118 static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
84119 {
84120 int i;
84121 drflac_uint32 riceParamMask;
84122 drflac_int32* pDecodedSamples = pSamplesOut;
84123 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
84124 drflac_uint32 zeroCountParts[4];
84125 drflac_uint32 riceParamParts[4];
84126 int32x4_t coefficients128_0;
84127 int32x4_t coefficients128_4;
84128 int32x4_t coefficients128_8;
84129 int32x4_t samples128_0;
84130 int32x4_t samples128_4;
84131 int32x4_t samples128_8;
84132 uint32x4_t riceParamMask128;
84133 int32x4_t riceParam128;
84134 int32x2_t shift64;
84135 uint32x4_t one128;
84136 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
84137 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
84138 riceParamMask128 = vdupq_n_u32(riceParamMask);
84139 riceParam128 = vdupq_n_s32(riceParam);
84140 shift64 = vdup_n_s32(-shift);
84141 one128 = vdupq_n_u32(1);
84142 {
84143 int runningOrder = order;
84144 drflac_int32 tempC[4] = {0, 0, 0, 0};
84145 drflac_int32 tempS[4] = {0, 0, 0, 0};
84146 if (runningOrder >= 4) {
84147 coefficients128_0 = vld1q_s32(coefficients + 0);
84148 samples128_0 = vld1q_s32(pSamplesOut - 4);
84149 runningOrder -= 4;
84150 } else {
84151 switch (runningOrder) {
84152 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
84153 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
84154 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
84155 }
84156 coefficients128_0 = vld1q_s32(tempC);
84157 samples128_0 = vld1q_s32(tempS);
84158 runningOrder = 0;
84159 }
84160 if (runningOrder >= 4) {
84161 coefficients128_4 = vld1q_s32(coefficients + 4);
84162 samples128_4 = vld1q_s32(pSamplesOut - 8);
84163 runningOrder -= 4;
84164 } else {
84165 switch (runningOrder) {
84166 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
84167 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
84168 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
84169 }
84170 coefficients128_4 = vld1q_s32(tempC);
84171 samples128_4 = vld1q_s32(tempS);
84172 runningOrder = 0;
84173 }
84174 if (runningOrder == 4) {
84175 coefficients128_8 = vld1q_s32(coefficients + 8);
84176 samples128_8 = vld1q_s32(pSamplesOut - 12);
84177 runningOrder -= 4;
84178 } else {
84179 switch (runningOrder) {
84180 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
84181 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
84182 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
84183 }
84184 coefficients128_8 = vld1q_s32(tempC);
84185 samples128_8 = vld1q_s32(tempS);
84186 runningOrder = 0;
84187 }
84188 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
84189 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
84190 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
84191 }
84192 while (pDecodedSamples < pDecodedSamplesEnd) {
84193 int32x4_t prediction128;
84194 int32x2_t prediction64;
84195 uint32x4_t zeroCountPart128;
84196 uint32x4_t riceParamPart128;
84197 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
84198 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
84199 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
84200 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
84201 return DRFLAC_FALSE;
84202 }
84203 zeroCountPart128 = vld1q_u32(zeroCountParts);
84204 riceParamPart128 = vld1q_u32(riceParamParts);
84205 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
84206 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
84207 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
84208 if (order <= 4) {
84209 for (i = 0; i < 4; i += 1) {
84210 prediction128 = vmulq_s32(coefficients128_0, samples128_0);
84211 prediction64 = drflac__vhaddq_s32(prediction128);
84212 prediction64 = vshl_s32(prediction64, shift64);
84213 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
84214 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
84215 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84216 }
84217 } else if (order <= 8) {
84218 for (i = 0; i < 4; i += 1) {
84219 prediction128 = vmulq_s32(coefficients128_4, samples128_4);
84220 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
84221 prediction64 = drflac__vhaddq_s32(prediction128);
84222 prediction64 = vshl_s32(prediction64, shift64);
84223 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
84224 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
84225 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
84226 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84227 }
84228 } else {
84229 for (i = 0; i < 4; i += 1) {
84230 prediction128 = vmulq_s32(coefficients128_8, samples128_8);
84231 prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);
84232 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
84233 prediction64 = drflac__vhaddq_s32(prediction128);
84234 prediction64 = vshl_s32(prediction64, shift64);
84235 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
84236 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
84237 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
84238 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
84239 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84240 }
84241 }
84242 vst1q_s32(pDecodedSamples, samples128_0);
84243 pDecodedSamples += 4;
84244 }
84245 i = (count & ~3);
84246 while (i < (int)count) {
84247 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
84248 return DRFLAC_FALSE;
84249 }
84250 riceParamParts[0] &= riceParamMask;
84251 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
84252 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
84253 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
84254 i += 1;
84255 pDecodedSamples += 1;
84256 }
84257 return DRFLAC_TRUE;
84258 }
84259 static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
84260 {
84261 int i;
84262 drflac_uint32 riceParamMask;
84263 drflac_int32* pDecodedSamples = pSamplesOut;
84264 drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
84265 drflac_uint32 zeroCountParts[4];
84266 drflac_uint32 riceParamParts[4];
84267 int32x4_t coefficients128_0;
84268 int32x4_t coefficients128_4;
84269 int32x4_t coefficients128_8;
84270 int32x4_t samples128_0;
84271 int32x4_t samples128_4;
84272 int32x4_t samples128_8;
84273 uint32x4_t riceParamMask128;
84274 int32x4_t riceParam128;
84275 int64x1_t shift64;
84276 uint32x4_t one128;
84277 int64x2_t prediction128 = { 0 };
84278 uint32x4_t zeroCountPart128;
84279 uint32x4_t riceParamPart128;
84280 const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
84281 riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
84282 riceParamMask128 = vdupq_n_u32(riceParamMask);
84283 riceParam128 = vdupq_n_s32(riceParam);
84284 shift64 = vdup_n_s64(-shift);
84285 one128 = vdupq_n_u32(1);
84286 {
84287 int runningOrder = order;
84288 drflac_int32 tempC[4] = {0, 0, 0, 0};
84289 drflac_int32 tempS[4] = {0, 0, 0, 0};
84290 if (runningOrder >= 4) {
84291 coefficients128_0 = vld1q_s32(coefficients + 0);
84292 samples128_0 = vld1q_s32(pSamplesOut - 4);
84293 runningOrder -= 4;
84294 } else {
84295 switch (runningOrder) {
84296 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
84297 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
84298 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
84299 }
84300 coefficients128_0 = vld1q_s32(tempC);
84301 samples128_0 = vld1q_s32(tempS);
84302 runningOrder = 0;
84303 }
84304 if (runningOrder >= 4) {
84305 coefficients128_4 = vld1q_s32(coefficients + 4);
84306 samples128_4 = vld1q_s32(pSamplesOut - 8);
84307 runningOrder -= 4;
84308 } else {
84309 switch (runningOrder) {
84310 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
84311 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
84312 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
84313 }
84314 coefficients128_4 = vld1q_s32(tempC);
84315 samples128_4 = vld1q_s32(tempS);
84316 runningOrder = 0;
84317 }
84318 if (runningOrder == 4) {
84319 coefficients128_8 = vld1q_s32(coefficients + 8);
84320 samples128_8 = vld1q_s32(pSamplesOut - 12);
84321 runningOrder -= 4;
84322 } else {
84323 switch (runningOrder) {
84324 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
84325 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
84326 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
84327 }
84328 coefficients128_8 = vld1q_s32(tempC);
84329 samples128_8 = vld1q_s32(tempS);
84330 runningOrder = 0;
84331 }
84332 coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
84333 coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
84334 coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
84335 }
84336 while (pDecodedSamples < pDecodedSamplesEnd) {
84337 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
84338 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
84339 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
84340 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
84341 return DRFLAC_FALSE;
84342 }
84343 zeroCountPart128 = vld1q_u32(zeroCountParts);
84344 riceParamPart128 = vld1q_u32(riceParamParts);
84345 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
84346 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
84347 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
84348 for (i = 0; i < 4; i += 1) {
84349 int64x1_t prediction64;
84350 prediction128 = veorq_s64(prediction128, prediction128);
84351 switch (order)
84352 {
84353 case 12:
84354 case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
84355 case 10:
84356 case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
84357 case 8:
84358 case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
84359 case 6:
84360 case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
84361 case 4:
84362 case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
84363 case 2:
84364 case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
84365 }
84366 prediction64 = drflac__vhaddq_s64(prediction128);
84367 prediction64 = vshl_s64(prediction64, shift64);
84368 prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
84369 samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
84370 samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
84371 samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
84372 riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
84373 }
84374 vst1q_s32(pDecodedSamples, samples128_0);
84375 pDecodedSamples += 4;
84376 }
84377 i = (count & ~3);
84378 while (i < (int)count) {
84379 if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
84380 return DRFLAC_FALSE;
84381 }
84382 riceParamParts[0] &= riceParamMask;
84383 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
84384 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
84385 pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
84386 i += 1;
84387 pDecodedSamples += 1;
84388 }
84389 return DRFLAC_TRUE;
84390 }
84391 static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
84392 {
84393 DRFLAC_ASSERT(bs != NULL);
84394 DRFLAC_ASSERT(pSamplesOut != NULL);
84395 if (lpcOrder > 0 && lpcOrder <= 12) {
84396 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
84397 return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84398 } else {
84399 return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
84400 }
84401 } else {
84402 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84403 }
84404 }
84405 #endif
84406 static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
84407 {
84408 #if defined(DRFLAC_SUPPORT_SSE41)
84409 if (drflac__gIsSSE41Supported) {
84410 return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84411 } else
84412 #elif defined(DRFLAC_SUPPORT_NEON)
84413 if (drflac__gIsNEONSupported) {
84414 return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84415 } else
84416 #endif
84417 {
84418 #if 0
84419 return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84420 #else
84421 return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
84422 #endif
84423 }
84424 }
84425 static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam)
84426 {
84427 drflac_uint32 i;
84428 DRFLAC_ASSERT(bs != NULL);
84429 for (i = 0; i < count; ++i) {
84430 if (!drflac__seek_rice_parts(bs, riceParam)) {
84431 return DRFLAC_FALSE;
84432 }
84433 }
84434 return DRFLAC_TRUE;
84435 }
84436 #if defined(__clang__)
84437 __attribute__((no_sanitize("signed-integer-overflow")))
84438 #endif
84439 static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
84440 {
84441 drflac_uint32 i;
84442 DRFLAC_ASSERT(bs != NULL);
84443 DRFLAC_ASSERT(unencodedBitsPerSample <= 31);
84444 DRFLAC_ASSERT(pSamplesOut != NULL);
84445 for (i = 0; i < count; ++i) {
84446 if (unencodedBitsPerSample > 0) {
84447 if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
84448 return DRFLAC_FALSE;
84449 }
84450 } else {
84451 pSamplesOut[i] = 0;
84452 }
84453 if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
84454 pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
84455 } else {
84456 pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
84457 }
84458 }
84459 return DRFLAC_TRUE;
84460 }
84461 static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
84462 {
84463 drflac_uint8 residualMethod;
84464 drflac_uint8 partitionOrder;
84465 drflac_uint32 samplesInPartition;
84466 drflac_uint32 partitionsRemaining;
84467 DRFLAC_ASSERT(bs != NULL);
84468 DRFLAC_ASSERT(blockSize != 0);
84469 DRFLAC_ASSERT(pDecodedSamples != NULL);
84470 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
84471 return DRFLAC_FALSE;
84472 }
84473 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84474 return DRFLAC_FALSE;
84475 }
84476 pDecodedSamples += lpcOrder;
84477 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
84478 return DRFLAC_FALSE;
84479 }
84480 if (partitionOrder > 8) {
84481 return DRFLAC_FALSE;
84482 }
84483 if ((blockSize / (1 << partitionOrder)) < lpcOrder) {
84484 return DRFLAC_FALSE;
84485 }
84486 samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder;
84487 partitionsRemaining = (1 << partitionOrder);
84488 for (;;) {
84489 drflac_uint8 riceParam = 0;
84490 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
84491 if (!drflac__read_uint8(bs, 4, &riceParam)) {
84492 return DRFLAC_FALSE;
84493 }
84494 if (riceParam == 15) {
84495 riceParam = 0xFF;
84496 }
84497 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84498 if (!drflac__read_uint8(bs, 5, &riceParam)) {
84499 return DRFLAC_FALSE;
84500 }
84501 if (riceParam == 31) {
84502 riceParam = 0xFF;
84503 }
84504 }
84505 if (riceParam != 0xFF) {
84506 if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
84507 return DRFLAC_FALSE;
84508 }
84509 } else {
84510 drflac_uint8 unencodedBitsPerSample = 0;
84511 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
84512 return DRFLAC_FALSE;
84513 }
84514 if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
84515 return DRFLAC_FALSE;
84516 }
84517 }
84518 pDecodedSamples += samplesInPartition;
84519 if (partitionsRemaining == 1) {
84520 break;
84521 }
84522 partitionsRemaining -= 1;
84523 if (partitionOrder != 0) {
84524 samplesInPartition = blockSize / (1 << partitionOrder);
84525 }
84526 }
84527 return DRFLAC_TRUE;
84528 }
84529 static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order)
84530 {
84531 drflac_uint8 residualMethod;
84532 drflac_uint8 partitionOrder;
84533 drflac_uint32 samplesInPartition;
84534 drflac_uint32 partitionsRemaining;
84535 DRFLAC_ASSERT(bs != NULL);
84536 DRFLAC_ASSERT(blockSize != 0);
84537 if (!drflac__read_uint8(bs, 2, &residualMethod)) {
84538 return DRFLAC_FALSE;
84539 }
84540 if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84541 return DRFLAC_FALSE;
84542 }
84543 if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
84544 return DRFLAC_FALSE;
84545 }
84546 if (partitionOrder > 8) {
84547 return DRFLAC_FALSE;
84548 }
84549 if ((blockSize / (1 << partitionOrder)) <= order) {
84550 return DRFLAC_FALSE;
84551 }
84552 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
84553 partitionsRemaining = (1 << partitionOrder);
84554 for (;;)
84555 {
84556 drflac_uint8 riceParam = 0;
84557 if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
84558 if (!drflac__read_uint8(bs, 4, &riceParam)) {
84559 return DRFLAC_FALSE;
84560 }
84561 if (riceParam == 15) {
84562 riceParam = 0xFF;
84563 }
84564 } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
84565 if (!drflac__read_uint8(bs, 5, &riceParam)) {
84566 return DRFLAC_FALSE;
84567 }
84568 if (riceParam == 31) {
84569 riceParam = 0xFF;
84570 }
84571 }
84572 if (riceParam != 0xFF) {
84573 if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
84574 return DRFLAC_FALSE;
84575 }
84576 } else {
84577 drflac_uint8 unencodedBitsPerSample = 0;
84578 if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
84579 return DRFLAC_FALSE;
84580 }
84581 if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
84582 return DRFLAC_FALSE;
84583 }
84584 }
84585 if (partitionsRemaining == 1) {
84586 break;
84587 }
84588 partitionsRemaining -= 1;
84589 samplesInPartition = blockSize / (1 << partitionOrder);
84590 }
84591 return DRFLAC_TRUE;
84592 }
84593 static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
84594 {
84595 drflac_uint32 i;
84596 drflac_int32 sample;
84597 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
84598 return DRFLAC_FALSE;
84599 }
84600 for (i = 0; i < blockSize; ++i) {
84601 pDecodedSamples[i] = sample;
84602 }
84603 return DRFLAC_TRUE;
84604 }
84605 static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples)
84606 {
84607 drflac_uint32 i;
84608 for (i = 0; i < blockSize; ++i) {
84609 drflac_int32 sample;
84610 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
84611 return DRFLAC_FALSE;
84612 }
84613 pDecodedSamples[i] = sample;
84614 }
84615 return DRFLAC_TRUE;
84616 }
84617 static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
84618 {
84619 drflac_uint32 i;
84620 static drflac_int32 lpcCoefficientsTable[5][4] = {
84621 {0, 0, 0, 0},
84622 {1, 0, 0, 0},
84623 {2, -1, 0, 0},
84624 {3, -3, 1, 0},
84625 {4, -6, 4, -1}
84626 };
84627 for (i = 0; i < lpcOrder; ++i) {
84628 drflac_int32 sample;
84629 if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
84630 return DRFLAC_FALSE;
84631 }
84632 pDecodedSamples[i] = sample;
84633 }
84634 if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
84635 return DRFLAC_FALSE;
84636 }
84637 return DRFLAC_TRUE;
84638 }
84639 static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
84640 {
84641 drflac_uint8 i;
84642 drflac_uint8 lpcPrecision;
84643 drflac_int8 lpcShift;
84644 drflac_int32 coefficients[32];
84645 for (i = 0; i < lpcOrder; ++i) {
84646 drflac_int32 sample;
84647 if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
84648 return DRFLAC_FALSE;
84649 }
84650 pDecodedSamples[i] = sample;
84651 }
84652 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
84653 return DRFLAC_FALSE;
84654 }
84655 if (lpcPrecision == 15) {
84656 return DRFLAC_FALSE;
84657 }
84658 lpcPrecision += 1;
84659 if (!drflac__read_int8(bs, 5, &lpcShift)) {
84660 return DRFLAC_FALSE;
84661 }
84662 if (lpcShift < 0) {
84663 return DRFLAC_FALSE;
84664 }
84665 DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
84666 for (i = 0; i < lpcOrder; ++i) {
84667 if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
84668 return DRFLAC_FALSE;
84669 }
84670 }
84671 if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
84672 return DRFLAC_FALSE;
84673 }
84674 return DRFLAC_TRUE;
84675 }
84676 static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
84677 {
84678 const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
84679 const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1};
84680 DRFLAC_ASSERT(bs != NULL);
84681 DRFLAC_ASSERT(header != NULL);
84682 for (;;) {
84683 drflac_uint8 crc8 = 0xCE;
84684 drflac_uint8 reserved = 0;
84685 drflac_uint8 blockingStrategy = 0;
84686 drflac_uint8 blockSize = 0;
84687 drflac_uint8 sampleRate = 0;
84688 drflac_uint8 channelAssignment = 0;
84689 drflac_uint8 bitsPerSample = 0;
84690 drflac_bool32 isVariableBlockSize;
84691 if (!drflac__find_and_seek_to_next_sync_code(bs)) {
84692 return DRFLAC_FALSE;
84693 }
84694 if (!drflac__read_uint8(bs, 1, &reserved)) {
84695 return DRFLAC_FALSE;
84696 }
84697 if (reserved == 1) {
84698 continue;
84699 }
84700 crc8 = drflac_crc8(crc8, reserved, 1);
84701 if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
84702 return DRFLAC_FALSE;
84703 }
84704 crc8 = drflac_crc8(crc8, blockingStrategy, 1);
84705 if (!drflac__read_uint8(bs, 4, &blockSize)) {
84706 return DRFLAC_FALSE;
84707 }
84708 if (blockSize == 0) {
84709 continue;
84710 }
84711 crc8 = drflac_crc8(crc8, blockSize, 4);
84712 if (!drflac__read_uint8(bs, 4, &sampleRate)) {
84713 return DRFLAC_FALSE;
84714 }
84715 crc8 = drflac_crc8(crc8, sampleRate, 4);
84716 if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
84717 return DRFLAC_FALSE;
84718 }
84719 if (channelAssignment > 10) {
84720 continue;
84721 }
84722 crc8 = drflac_crc8(crc8, channelAssignment, 4);
84723 if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
84724 return DRFLAC_FALSE;
84725 }
84726 if (bitsPerSample == 3 || bitsPerSample == 7) {
84727 continue;
84728 }
84729 crc8 = drflac_crc8(crc8, bitsPerSample, 3);
84730 if (!drflac__read_uint8(bs, 1, &reserved)) {
84731 return DRFLAC_FALSE;
84732 }
84733 if (reserved == 1) {
84734 continue;
84735 }
84736 crc8 = drflac_crc8(crc8, reserved, 1);
84737 isVariableBlockSize = blockingStrategy == 1;
84738 if (isVariableBlockSize) {
84739 drflac_uint64 pcmFrameNumber;
84740 drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
84741 if (result != DRFLAC_SUCCESS) {
84742 if (result == DRFLAC_AT_END) {
84743 return DRFLAC_FALSE;
84744 } else {
84745 continue;
84746 }
84747 }
84748 header->flacFrameNumber = 0;
84749 header->pcmFrameNumber = pcmFrameNumber;
84750 } else {
84751 drflac_uint64 flacFrameNumber = 0;
84752 drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
84753 if (result != DRFLAC_SUCCESS) {
84754 if (result == DRFLAC_AT_END) {
84755 return DRFLAC_FALSE;
84756 } else {
84757 continue;
84758 }
84759 }
84760 header->flacFrameNumber = (drflac_uint32)flacFrameNumber;
84761 header->pcmFrameNumber = 0;
84762 }
84763 DRFLAC_ASSERT(blockSize > 0);
84764 if (blockSize == 1) {
84765 header->blockSizeInPCMFrames = 192;
84766 } else if (blockSize <= 5) {
84767 DRFLAC_ASSERT(blockSize >= 2);
84768 header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
84769 } else if (blockSize == 6) {
84770 if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
84771 return DRFLAC_FALSE;
84772 }
84773 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8);
84774 header->blockSizeInPCMFrames += 1;
84775 } else if (blockSize == 7) {
84776 if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
84777 return DRFLAC_FALSE;
84778 }
84779 crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16);
84780 if (header->blockSizeInPCMFrames == 0xFFFF) {
84781 return DRFLAC_FALSE;
84782 }
84783 header->blockSizeInPCMFrames += 1;
84784 } else {
84785 DRFLAC_ASSERT(blockSize >= 8);
84786 header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));
84787 }
84788 if (sampleRate <= 11) {
84789 header->sampleRate = sampleRateTable[sampleRate];
84790 } else if (sampleRate == 12) {
84791 if (!drflac__read_uint32(bs, 8, &header->sampleRate)) {
84792 return DRFLAC_FALSE;
84793 }
84794 crc8 = drflac_crc8(crc8, header->sampleRate, 8);
84795 header->sampleRate *= 1000;
84796 } else if (sampleRate == 13) {
84797 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
84798 return DRFLAC_FALSE;
84799 }
84800 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
84801 } else if (sampleRate == 14) {
84802 if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
84803 return DRFLAC_FALSE;
84804 }
84805 crc8 = drflac_crc8(crc8, header->sampleRate, 16);
84806 header->sampleRate *= 10;
84807 } else {
84808 continue;
84809 }
84810 header->channelAssignment = channelAssignment;
84811 header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
84812 if (header->bitsPerSample == 0) {
84813 header->bitsPerSample = streaminfoBitsPerSample;
84814 }
84815 if (header->bitsPerSample != streaminfoBitsPerSample) {
84816 return DRFLAC_FALSE;
84817 }
84818 if (!drflac__read_uint8(bs, 8, &header->crc8)) {
84819 return DRFLAC_FALSE;
84820 }
84821 #ifndef DR_FLAC_NO_CRC
84822 if (header->crc8 != crc8) {
84823 continue;
84824 }
84825 #endif
84826 return DRFLAC_TRUE;
84827 }
84828 }
84829 static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
84830 {
84831 drflac_uint8 header;
84832 int type;
84833 if (!drflac__read_uint8(bs, 8, &header)) {
84834 return DRFLAC_FALSE;
84835 }
84836 if ((header & 0x80) != 0) {
84837 return DRFLAC_FALSE;
84838 }
84839 type = (header & 0x7E) >> 1;
84840 if (type == 0) {
84841 pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
84842 } else if (type == 1) {
84843 pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM;
84844 } else {
84845 if ((type & 0x20) != 0) {
84846 pSubframe->subframeType = DRFLAC_SUBFRAME_LPC;
84847 pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1;
84848 } else if ((type & 0x08) != 0) {
84849 pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED;
84850 pSubframe->lpcOrder = (drflac_uint8)(type & 0x07);
84851 if (pSubframe->lpcOrder > 4) {
84852 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
84853 pSubframe->lpcOrder = 0;
84854 }
84855 } else {
84856 pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
84857 }
84858 }
84859 if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) {
84860 return DRFLAC_FALSE;
84861 }
84862 pSubframe->wastedBitsPerSample = 0;
84863 if ((header & 0x01) == 1) {
84864 unsigned int wastedBitsPerSample;
84865 if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
84866 return DRFLAC_FALSE;
84867 }
84868 pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1;
84869 }
84870 return DRFLAC_TRUE;
84871 }
84872 static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut)
84873 {
84874 drflac_subframe* pSubframe;
84875 drflac_uint32 subframeBitsPerSample;
84876 DRFLAC_ASSERT(bs != NULL);
84877 DRFLAC_ASSERT(frame != NULL);
84878 pSubframe = frame->subframes + subframeIndex;
84879 if (!drflac__read_subframe_header(bs, pSubframe)) {
84880 return DRFLAC_FALSE;
84881 }
84882 subframeBitsPerSample = frame->header.bitsPerSample;
84883 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
84884 subframeBitsPerSample += 1;
84885 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
84886 subframeBitsPerSample += 1;
84887 }
84888 if (subframeBitsPerSample > 32) {
84889 return DRFLAC_FALSE;
84890 }
84891 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
84892 return DRFLAC_FALSE;
84893 }
84894 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
84895 pSubframe->pSamplesS32 = pDecodedSamplesOut;
84896 switch (pSubframe->subframeType)
84897 {
84898 case DRFLAC_SUBFRAME_CONSTANT:
84899 {
84900 drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
84901 } break;
84902 case DRFLAC_SUBFRAME_VERBATIM:
84903 {
84904 drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
84905 } break;
84906 case DRFLAC_SUBFRAME_FIXED:
84907 {
84908 drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
84909 } break;
84910 case DRFLAC_SUBFRAME_LPC:
84911 {
84912 drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
84913 } break;
84914 default: return DRFLAC_FALSE;
84915 }
84916 return DRFLAC_TRUE;
84917 }
84918 static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
84919 {
84920 drflac_subframe* pSubframe;
84921 drflac_uint32 subframeBitsPerSample;
84922 DRFLAC_ASSERT(bs != NULL);
84923 DRFLAC_ASSERT(frame != NULL);
84924 pSubframe = frame->subframes + subframeIndex;
84925 if (!drflac__read_subframe_header(bs, pSubframe)) {
84926 return DRFLAC_FALSE;
84927 }
84928 subframeBitsPerSample = frame->header.bitsPerSample;
84929 if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
84930 subframeBitsPerSample += 1;
84931 } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
84932 subframeBitsPerSample += 1;
84933 }
84934 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
84935 return DRFLAC_FALSE;
84936 }
84937 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
84938 pSubframe->pSamplesS32 = NULL;
84939 switch (pSubframe->subframeType)
84940 {
84941 case DRFLAC_SUBFRAME_CONSTANT:
84942 {
84943 if (!drflac__seek_bits(bs, subframeBitsPerSample)) {
84944 return DRFLAC_FALSE;
84945 }
84946 } break;
84947 case DRFLAC_SUBFRAME_VERBATIM:
84948 {
84949 unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
84950 if (!drflac__seek_bits(bs, bitsToSeek)) {
84951 return DRFLAC_FALSE;
84952 }
84953 } break;
84954 case DRFLAC_SUBFRAME_FIXED:
84955 {
84956 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
84957 if (!drflac__seek_bits(bs, bitsToSeek)) {
84958 return DRFLAC_FALSE;
84959 }
84960 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
84961 return DRFLAC_FALSE;
84962 }
84963 } break;
84964 case DRFLAC_SUBFRAME_LPC:
84965 {
84966 drflac_uint8 lpcPrecision;
84967 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
84968 if (!drflac__seek_bits(bs, bitsToSeek)) {
84969 return DRFLAC_FALSE;
84970 }
84971 if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
84972 return DRFLAC_FALSE;
84973 }
84974 if (lpcPrecision == 15) {
84975 return DRFLAC_FALSE;
84976 }
84977 lpcPrecision += 1;
84978 bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;
84979 if (!drflac__seek_bits(bs, bitsToSeek)) {
84980 return DRFLAC_FALSE;
84981 }
84982 if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
84983 return DRFLAC_FALSE;
84984 }
84985 } break;
84986 default: return DRFLAC_FALSE;
84987 }
84988 return DRFLAC_TRUE;
84989 }
84990 static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment)
84991 {
84992 drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
84993 DRFLAC_ASSERT(channelAssignment <= 10);
84994 return lookup[channelAssignment];
84995 }
84996 static drflac_result drflac__decode_flac_frame(drflac* pFlac)
84997 {
84998 int channelCount;
84999 int i;
85000 drflac_uint8 paddingSizeInBits;
85001 drflac_uint16 desiredCRC16;
85002 #ifndef DR_FLAC_NO_CRC
85003 drflac_uint16 actualCRC16;
85004 #endif
85005 DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
85006 if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {
85007 return DRFLAC_ERROR;
85008 }
85009 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
85010 if (channelCount != (int)pFlac->channels) {
85011 return DRFLAC_ERROR;
85012 }
85013 for (i = 0; i < channelCount; ++i) {
85014 if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
85015 return DRFLAC_ERROR;
85016 }
85017 }
85018 paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);
85019 if (paddingSizeInBits > 0) {
85020 drflac_uint8 padding = 0;
85021 if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
85022 return DRFLAC_AT_END;
85023 }
85024 }
85025 #ifndef DR_FLAC_NO_CRC
85026 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
85027 #endif
85028 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
85029 return DRFLAC_AT_END;
85030 }
85031 #ifndef DR_FLAC_NO_CRC
85032 if (actualCRC16 != desiredCRC16) {
85033 return DRFLAC_CRC_MISMATCH;
85034 }
85035 #endif
85036 pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
85037 return DRFLAC_SUCCESS;
85038 }
85039 static drflac_result drflac__seek_flac_frame(drflac* pFlac)
85040 {
85041 int channelCount;
85042 int i;
85043 drflac_uint16 desiredCRC16;
85044 #ifndef DR_FLAC_NO_CRC
85045 drflac_uint16 actualCRC16;
85046 #endif
85047 channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
85048 for (i = 0; i < channelCount; ++i) {
85049 if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
85050 return DRFLAC_ERROR;
85051 }
85052 }
85053 if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
85054 return DRFLAC_ERROR;
85055 }
85056 #ifndef DR_FLAC_NO_CRC
85057 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
85058 #endif
85059 if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
85060 return DRFLAC_AT_END;
85061 }
85062 #ifndef DR_FLAC_NO_CRC
85063 if (actualCRC16 != desiredCRC16) {
85064 return DRFLAC_CRC_MISMATCH;
85065 }
85066 #endif
85067 return DRFLAC_SUCCESS;
85068 }
85069 static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac)
85070 {
85071 DRFLAC_ASSERT(pFlac != NULL);
85072 for (;;) {
85073 drflac_result result;
85074 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85075 return DRFLAC_FALSE;
85076 }
85077 result = drflac__decode_flac_frame(pFlac);
85078 if (result != DRFLAC_SUCCESS) {
85079 if (result == DRFLAC_CRC_MISMATCH) {
85080 continue;
85081 } else {
85082 return DRFLAC_FALSE;
85083 }
85084 }
85085 return DRFLAC_TRUE;
85086 }
85087 }
85088 static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame)
85089 {
85090 drflac_uint64 firstPCMFrame;
85091 drflac_uint64 lastPCMFrame;
85092 DRFLAC_ASSERT(pFlac != NULL);
85093 firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
85094 if (firstPCMFrame == 0) {
85095 firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames;
85096 }
85097 lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
85098 if (lastPCMFrame > 0) {
85099 lastPCMFrame -= 1;
85100 }
85101 if (pFirstPCMFrame) {
85102 *pFirstPCMFrame = firstPCMFrame;
85103 }
85104 if (pLastPCMFrame) {
85105 *pLastPCMFrame = lastPCMFrame;
85106 }
85107 }
85108 static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
85109 {
85110 drflac_bool32 result;
85111 DRFLAC_ASSERT(pFlac != NULL);
85112 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
85113 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
85114 pFlac->currentPCMFrame = 0;
85115 return result;
85116 }
85117 static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac)
85118 {
85119 DRFLAC_ASSERT(pFlac != NULL);
85120 return drflac__seek_flac_frame(pFlac);
85121 }
85122 static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek)
85123 {
85124 drflac_uint64 pcmFramesRead = 0;
85125 while (pcmFramesToSeek > 0) {
85126 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85127 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
85128 break;
85129 }
85130 } else {
85131 if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
85132 pcmFramesRead += pcmFramesToSeek;
85133 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek;
85134 pcmFramesToSeek = 0;
85135 } else {
85136 pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
85137 pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
85138 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
85139 }
85140 }
85141 }
85142 pFlac->currentPCMFrame += pcmFramesRead;
85143 return pcmFramesRead;
85144 }
85145 static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex)
85146 {
85147 drflac_bool32 isMidFrame = DRFLAC_FALSE;
85148 drflac_uint64 runningPCMFrameCount;
85149 DRFLAC_ASSERT(pFlac != NULL);
85150 if (pcmFrameIndex >= pFlac->currentPCMFrame) {
85151 runningPCMFrameCount = pFlac->currentPCMFrame;
85152 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85153 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85154 return DRFLAC_FALSE;
85155 }
85156 } else {
85157 isMidFrame = DRFLAC_TRUE;
85158 }
85159 } else {
85160 runningPCMFrameCount = 0;
85161 if (!drflac__seek_to_first_frame(pFlac)) {
85162 return DRFLAC_FALSE;
85163 }
85164 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85165 return DRFLAC_FALSE;
85166 }
85167 }
85168 for (;;) {
85169 drflac_uint64 pcmFrameCountInThisFLACFrame;
85170 drflac_uint64 firstPCMFrameInFLACFrame = 0;
85171 drflac_uint64 lastPCMFrameInFLACFrame = 0;
85172 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
85173 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
85174 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
85175 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
85176 if (!isMidFrame) {
85177 drflac_result result = drflac__decode_flac_frame(pFlac);
85178 if (result == DRFLAC_SUCCESS) {
85179 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85180 } else {
85181 if (result == DRFLAC_CRC_MISMATCH) {
85182 goto next_iteration;
85183 } else {
85184 return DRFLAC_FALSE;
85185 }
85186 }
85187 } else {
85188 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85189 }
85190 } else {
85191 if (!isMidFrame) {
85192 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
85193 if (result == DRFLAC_SUCCESS) {
85194 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
85195 } else {
85196 if (result == DRFLAC_CRC_MISMATCH) {
85197 goto next_iteration;
85198 } else {
85199 return DRFLAC_FALSE;
85200 }
85201 }
85202 } else {
85203 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
85204 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
85205 isMidFrame = DRFLAC_FALSE;
85206 }
85207 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
85208 return DRFLAC_TRUE;
85209 }
85210 }
85211 next_iteration:
85212 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85213 return DRFLAC_FALSE;
85214 }
85215 }
85216 }
85217 #if !defined(DR_FLAC_NO_CRC)
85218 #define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
85219 static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset)
85220 {
85221 DRFLAC_ASSERT(pFlac != NULL);
85222 DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
85223 DRFLAC_ASSERT(targetByte >= rangeLo);
85224 DRFLAC_ASSERT(targetByte <= rangeHi);
85225 *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
85226 for (;;) {
85227 drflac_uint64 lastTargetByte = targetByte;
85228 if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) {
85229 if (targetByte == 0) {
85230 drflac__seek_to_first_frame(pFlac);
85231 return DRFLAC_FALSE;
85232 }
85233 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
85234 rangeHi = targetByte;
85235 } else {
85236 DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
85237 #if 1
85238 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
85239 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
85240 rangeHi = targetByte;
85241 } else {
85242 break;
85243 }
85244 #else
85245 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85246 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
85247 rangeHi = targetByte;
85248 } else {
85249 break;
85250 }
85251 #endif
85252 }
85253 if(targetByte == lastTargetByte) {
85254 return DRFLAC_FALSE;
85255 }
85256 }
85257 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
85258 DRFLAC_ASSERT(targetByte <= rangeHi);
85259 *pLastSuccessfulSeekOffset = targetByte;
85260 return DRFLAC_TRUE;
85261 }
85262 static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset)
85263 {
85264 #if 0
85265 if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) {
85266 if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) {
85267 return DRFLAC_FALSE;
85268 }
85269 }
85270 #endif
85271 return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
85272 }
85273 static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi)
85274 {
85275 drflac_uint64 targetByte;
85276 drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
85277 drflac_uint64 pcmRangeHi = 0;
85278 drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1;
85279 drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
85280 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
85281 targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
85282 if (targetByte > byteRangeHi) {
85283 targetByte = byteRangeHi;
85284 }
85285 for (;;) {
85286 if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
85287 drflac_uint64 newPCMRangeLo;
85288 drflac_uint64 newPCMRangeHi;
85289 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
85290 if (pcmRangeLo == newPCMRangeLo) {
85291 if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
85292 break;
85293 }
85294 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
85295 return DRFLAC_TRUE;
85296 } else {
85297 break;
85298 }
85299 }
85300 pcmRangeLo = newPCMRangeLo;
85301 pcmRangeHi = newPCMRangeHi;
85302 if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
85303 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
85304 return DRFLAC_TRUE;
85305 } else {
85306 break;
85307 }
85308 } else {
85309 const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);
85310 if (pcmRangeLo > pcmFrameIndex) {
85311 byteRangeHi = lastSuccessfulSeekOffset;
85312 if (byteRangeLo > byteRangeHi) {
85313 byteRangeLo = byteRangeHi;
85314 }
85315 targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
85316 if (targetByte < byteRangeLo) {
85317 targetByte = byteRangeLo;
85318 }
85319 } else {
85320 if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
85321 if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
85322 return DRFLAC_TRUE;
85323 } else {
85324 break;
85325 }
85326 } else {
85327 byteRangeLo = lastSuccessfulSeekOffset;
85328 if (byteRangeHi < byteRangeLo) {
85329 byteRangeHi = byteRangeLo;
85330 }
85331 targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);
85332 if (targetByte > byteRangeHi) {
85333 targetByte = byteRangeHi;
85334 }
85335 if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
85336 closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
85337 }
85338 }
85339 }
85340 }
85341 } else {
85342 break;
85343 }
85344 }
85345 drflac__seek_to_first_frame(pFlac);
85346 return DRFLAC_FALSE;
85347 }
85348 static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex)
85349 {
85350 drflac_uint64 byteRangeLo;
85351 drflac_uint64 byteRangeHi;
85352 drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
85353 if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) {
85354 return DRFLAC_FALSE;
85355 }
85356 if (pcmFrameIndex < seekForwardThreshold) {
85357 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
85358 }
85359 byteRangeLo = pFlac->firstFLACFramePosInBytes;
85360 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
85361 return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
85362 }
85363 #endif
85364 static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex)
85365 {
85366 drflac_uint32 iClosestSeekpoint = 0;
85367 drflac_bool32 isMidFrame = DRFLAC_FALSE;
85368 drflac_uint64 runningPCMFrameCount;
85369 drflac_uint32 iSeekpoint;
85370 DRFLAC_ASSERT(pFlac != NULL);
85371 if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
85372 return DRFLAC_FALSE;
85373 }
85374 if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) {
85375 return DRFLAC_FALSE;
85376 }
85377 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
85378 if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
85379 break;
85380 }
85381 iClosestSeekpoint = iSeekpoint;
85382 }
85383 if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {
85384 return DRFLAC_FALSE;
85385 }
85386 if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {
85387 return DRFLAC_FALSE;
85388 }
85389 #if !defined(DR_FLAC_NO_CRC)
85390 if (pFlac->totalPCMFrameCount > 0) {
85391 drflac_uint64 byteRangeLo;
85392 drflac_uint64 byteRangeHi;
85393 byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
85394 byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
85395 if (iClosestSeekpoint < pFlac->seekpointCount-1) {
85396 drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1;
85397 if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {
85398 return DRFLAC_FALSE;
85399 }
85400 if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {
85401 byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;
85402 }
85403 }
85404 if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
85405 if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85406 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
85407 if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
85408 return DRFLAC_TRUE;
85409 }
85410 }
85411 }
85412 }
85413 #endif
85414 if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
85415 runningPCMFrameCount = pFlac->currentPCMFrame;
85416 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
85417 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85418 return DRFLAC_FALSE;
85419 }
85420 } else {
85421 isMidFrame = DRFLAC_TRUE;
85422 }
85423 } else {
85424 runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
85425 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
85426 return DRFLAC_FALSE;
85427 }
85428 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85429 return DRFLAC_FALSE;
85430 }
85431 }
85432 for (;;) {
85433 drflac_uint64 pcmFrameCountInThisFLACFrame;
85434 drflac_uint64 firstPCMFrameInFLACFrame = 0;
85435 drflac_uint64 lastPCMFrameInFLACFrame = 0;
85436 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
85437 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
85438 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
85439 drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
85440 if (!isMidFrame) {
85441 drflac_result result = drflac__decode_flac_frame(pFlac);
85442 if (result == DRFLAC_SUCCESS) {
85443 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85444 } else {
85445 if (result == DRFLAC_CRC_MISMATCH) {
85446 goto next_iteration;
85447 } else {
85448 return DRFLAC_FALSE;
85449 }
85450 }
85451 } else {
85452 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
85453 }
85454 } else {
85455 if (!isMidFrame) {
85456 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
85457 if (result == DRFLAC_SUCCESS) {
85458 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
85459 } else {
85460 if (result == DRFLAC_CRC_MISMATCH) {
85461 goto next_iteration;
85462 } else {
85463 return DRFLAC_FALSE;
85464 }
85465 }
85466 } else {
85467 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
85468 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
85469 isMidFrame = DRFLAC_FALSE;
85470 }
85471 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
85472 return DRFLAC_TRUE;
85473 }
85474 }
85475 next_iteration:
85476 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
85477 return DRFLAC_FALSE;
85478 }
85479 }
85480 }
85481 #ifndef DR_FLAC_NO_OGG
85482 typedef struct
85483 {
85484 drflac_uint8 capturePattern[4];
85485 drflac_uint8 structureVersion;
85486 drflac_uint8 headerType;
85487 drflac_uint64 granulePosition;
85488 drflac_uint32 serialNumber;
85489 drflac_uint32 sequenceNumber;
85490 drflac_uint32 checksum;
85491 drflac_uint8 segmentCount;
85492 drflac_uint8 segmentTable[255];
85493 } drflac_ogg_page_header;
85494 #endif
85495 typedef struct
85496 {
85497 drflac_read_proc onRead;
85498 drflac_seek_proc onSeek;
85499 drflac_meta_proc onMeta;
85500 drflac_container container;
85501 void* pUserData;
85502 void* pUserDataMD;
85503 drflac_uint32 sampleRate;
85504 drflac_uint8 channels;
85505 drflac_uint8 bitsPerSample;
85506 drflac_uint64 totalPCMFrameCount;
85507 drflac_uint16 maxBlockSizeInPCMFrames;
85508 drflac_uint64 runningFilePos;
85509 drflac_bool32 hasStreamInfoBlock;
85510 drflac_bool32 hasMetadataBlocks;
85511 drflac_bs bs;
85512 drflac_frame_header firstFrameHeader;
85513 #ifndef DR_FLAC_NO_OGG
85514 drflac_uint32 oggSerial;
85515 drflac_uint64 oggFirstBytePos;
85516 drflac_ogg_page_header oggBosHeader;
85517 #endif
85518 } drflac_init_info;
85519 static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
85520 {
85521 blockHeader = drflac__be2host_32(blockHeader);
85522 *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31);
85523 *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24);
85524 *blockSize = (blockHeader & 0x00FFFFFFUL);
85525 }
85526 static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
85527 {
85528 drflac_uint32 blockHeader;
85529 *blockSize = 0;
85530 if (onRead(pUserData, &blockHeader, 4) != 4) {
85531 return DRFLAC_FALSE;
85532 }
85533 drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
85534 return DRFLAC_TRUE;
85535 }
85536 static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
85537 {
85538 drflac_uint32 blockSizes;
85539 drflac_uint64 frameSizes = 0;
85540 drflac_uint64 importantProps;
85541 drflac_uint8 md5[16];
85542 if (onRead(pUserData, &blockSizes, 4) != 4) {
85543 return DRFLAC_FALSE;
85544 }
85545 if (onRead(pUserData, &frameSizes, 6) != 6) {
85546 return DRFLAC_FALSE;
85547 }
85548 if (onRead(pUserData, &importantProps, 8) != 8) {
85549 return DRFLAC_FALSE;
85550 }
85551 if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
85552 return DRFLAC_FALSE;
85553 }
85554 blockSizes = drflac__be2host_32(blockSizes);
85555 frameSizes = drflac__be2host_64(frameSizes);
85556 importantProps = drflac__be2host_64(importantProps);
85557 pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16);
85558 pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF);
85559 pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40);
85560 pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16);
85561 pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44);
85562 pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
85563 pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
85564 pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
85565 DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
85566 return DRFLAC_TRUE;
85567 }
85568 static void* drflac__malloc_default(size_t sz, void* pUserData)
85569 {
85570 (void)pUserData;
85571 return DRFLAC_MALLOC(sz);
85572 }
85573 static void* drflac__realloc_default(void* p, size_t sz, void* pUserData)
85574 {
85575 (void)pUserData;
85576 return DRFLAC_REALLOC(p, sz);
85577 }
85578 static void drflac__free_default(void* p, void* pUserData)
85579 {
85580 (void)pUserData;
85581 DRFLAC_FREE(p);
85582 }
85583 static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks)
85584 {
85585 if (pAllocationCallbacks == NULL) {
85586 return NULL;
85587 }
85588 if (pAllocationCallbacks->onMalloc != NULL) {
85589 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
85590 }
85591 if (pAllocationCallbacks->onRealloc != NULL) {
85592 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
85593 }
85594 return NULL;
85595 }
85596 static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks)
85597 {
85598 if (pAllocationCallbacks == NULL) {
85599 return NULL;
85600 }
85601 if (pAllocationCallbacks->onRealloc != NULL) {
85602 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
85603 }
85604 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
85605 void* p2;
85606 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
85607 if (p2 == NULL) {
85608 return NULL;
85609 }
85610 if (p != NULL) {
85611 DRFLAC_COPY_MEMORY(p2, p, szOld);
85612 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
85613 }
85614 return p2;
85615 }
85616 return NULL;
85617 }
85618 static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
85619 {
85620 if (p == NULL || pAllocationCallbacks == NULL) {
85621 return;
85622 }
85623 if (pAllocationCallbacks->onFree != NULL) {
85624 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
85625 }
85626 }
85627 static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks)
85628 {
85629 drflac_uint64 runningFilePos = 42;
85630 drflac_uint64 seektablePos = 0;
85631 drflac_uint32 seektableSize = 0;
85632 for (;;) {
85633 drflac_metadata metadata;
85634 drflac_uint8 isLastBlock = 0;
85635 drflac_uint8 blockType;
85636 drflac_uint32 blockSize;
85637 if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) {
85638 return DRFLAC_FALSE;
85639 }
85640 runningFilePos += 4;
85641 metadata.type = blockType;
85642 metadata.pRawData = NULL;
85643 metadata.rawDataSize = 0;
85644 switch (blockType)
85645 {
85646 case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
85647 {
85648 if (blockSize < 4) {
85649 return DRFLAC_FALSE;
85650 }
85651 if (onMeta) {
85652 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
85653 if (pRawData == NULL) {
85654 return DRFLAC_FALSE;
85655 }
85656 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
85657 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85658 return DRFLAC_FALSE;
85659 }
85660 metadata.pRawData = pRawData;
85661 metadata.rawDataSize = blockSize;
85662 metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData);
85663 metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32));
85664 metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32);
85665 onMeta(pUserDataMD, &metadata);
85666 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85667 }
85668 } break;
85669 case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
85670 {
85671 seektablePos = runningFilePos;
85672 seektableSize = blockSize;
85673 if (onMeta) {
85674 drflac_uint32 seekpointCount;
85675 drflac_uint32 iSeekpoint;
85676 void* pRawData;
85677 seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES;
85678 pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks);
85679 if (pRawData == NULL) {
85680 return DRFLAC_FALSE;
85681 }
85682 for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) {
85683 drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
85684 if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) {
85685 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85686 return DRFLAC_FALSE;
85687 }
85688 pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame);
85689 pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset);
85690 pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount);
85691 }
85692 metadata.pRawData = pRawData;
85693 metadata.rawDataSize = blockSize;
85694 metadata.data.seektable.seekpointCount = seekpointCount;
85695 metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
85696 onMeta(pUserDataMD, &metadata);
85697 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85698 }
85699 } break;
85700 case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
85701 {
85702 if (blockSize < 8) {
85703 return DRFLAC_FALSE;
85704 }
85705 if (onMeta) {
85706 void* pRawData;
85707 const char* pRunningData;
85708 const char* pRunningDataEnd;
85709 drflac_uint32 i;
85710 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
85711 if (pRawData == NULL) {
85712 return DRFLAC_FALSE;
85713 }
85714 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
85715 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85716 return DRFLAC_FALSE;
85717 }
85718 metadata.pRawData = pRawData;
85719 metadata.rawDataSize = blockSize;
85720 pRunningData = (const char*)pRawData;
85721 pRunningDataEnd = (const char*)pRawData + blockSize;
85722 metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85723 if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) {
85724 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85725 return DRFLAC_FALSE;
85726 }
85727 metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
85728 metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85729 if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) {
85730 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85731 return DRFLAC_FALSE;
85732 }
85733 metadata.data.vorbis_comment.pComments = pRunningData;
85734 for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
85735 drflac_uint32 commentLength;
85736 if (pRunningDataEnd - pRunningData < 4) {
85737 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85738 return DRFLAC_FALSE;
85739 }
85740 commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85741 if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) {
85742 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85743 return DRFLAC_FALSE;
85744 }
85745 pRunningData += commentLength;
85746 }
85747 onMeta(pUserDataMD, &metadata);
85748 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85749 }
85750 } break;
85751 case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
85752 {
85753 if (blockSize < 396) {
85754 return DRFLAC_FALSE;
85755 }
85756 if (onMeta) {
85757 void* pRawData;
85758 const char* pRunningData;
85759 const char* pRunningDataEnd;
85760 size_t bufferSize;
85761 drflac_uint8 iTrack;
85762 drflac_uint8 iIndex;
85763 void* pTrackData;
85764 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
85765 if (pRawData == NULL) {
85766 return DRFLAC_FALSE;
85767 }
85768 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
85769 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85770 return DRFLAC_FALSE;
85771 }
85772 metadata.pRawData = pRawData;
85773 metadata.rawDataSize = blockSize;
85774 pRunningData = (const char*)pRawData;
85775 pRunningDataEnd = (const char*)pRawData + blockSize;
85776 DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
85777 metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
85778 metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
85779 metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
85780 metadata.data.cuesheet.pTrackData = NULL;
85781 {
85782 const char* pRunningDataSaved = pRunningData;
85783 bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES;
85784 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
85785 drflac_uint8 indexCount;
85786 drflac_uint32 indexPointSize;
85787 if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) {
85788 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85789 return DRFLAC_FALSE;
85790 }
85791 pRunningData += 35;
85792 indexCount = pRunningData[0];
85793 pRunningData += 1;
85794 bufferSize += indexCount * sizeof(drflac_cuesheet_track_index);
85795 indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
85796 if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
85797 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85798 return DRFLAC_FALSE;
85799 }
85800 pRunningData += indexPointSize;
85801 }
85802 pRunningData = pRunningDataSaved;
85803 }
85804 {
85805 char* pRunningTrackData;
85806 pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks);
85807 if (pTrackData == NULL) {
85808 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85809 return DRFLAC_FALSE;
85810 }
85811 pRunningTrackData = (char*)pTrackData;
85812 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
85813 drflac_uint8 indexCount;
85814 DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES);
85815 pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
85816 pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
85817 indexCount = pRunningData[0];
85818 pRunningData += 1;
85819 pRunningTrackData += 1;
85820 for (iIndex = 0; iIndex < indexCount; ++iIndex) {
85821 drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData;
85822 DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES);
85823 pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
85824 pRunningTrackData += sizeof(drflac_cuesheet_track_index);
85825 pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset);
85826 }
85827 }
85828 metadata.data.cuesheet.pTrackData = pTrackData;
85829 }
85830 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85831 pRawData = NULL;
85832 onMeta(pUserDataMD, &metadata);
85833 drflac__free_from_callbacks(pTrackData, pAllocationCallbacks);
85834 pTrackData = NULL;
85835 }
85836 } break;
85837 case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
85838 {
85839 if (blockSize < 32) {
85840 return DRFLAC_FALSE;
85841 }
85842 if (onMeta) {
85843 void* pRawData;
85844 const char* pRunningData;
85845 const char* pRunningDataEnd;
85846 pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
85847 if (pRawData == NULL) {
85848 return DRFLAC_FALSE;
85849 }
85850 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
85851 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85852 return DRFLAC_FALSE;
85853 }
85854 metadata.pRawData = pRawData;
85855 metadata.rawDataSize = blockSize;
85856 pRunningData = (const char*)pRawData;
85857 pRunningDataEnd = (const char*)pRawData + blockSize;
85858 metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85859 metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85860 if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) {
85861 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85862 return DRFLAC_FALSE;
85863 }
85864 metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
85865 metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85866 if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) {
85867 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85868 return DRFLAC_FALSE;
85869 }
85870 metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
85871 metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85872 metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85873 metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85874 metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85875 metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
85876 metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
85877 if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) {
85878 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85879 return DRFLAC_FALSE;
85880 }
85881 onMeta(pUserDataMD, &metadata);
85882 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85883 }
85884 } break;
85885 case DRFLAC_METADATA_BLOCK_TYPE_PADDING:
85886 {
85887 if (onMeta) {
85888 metadata.data.padding.unused = 0;
85889 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
85890 isLastBlock = DRFLAC_TRUE;
85891 } else {
85892 onMeta(pUserDataMD, &metadata);
85893 }
85894 }
85895 } break;
85896 case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
85897 {
85898 if (onMeta) {
85899 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
85900 isLastBlock = DRFLAC_TRUE;
85901 }
85902 }
85903 } break;
85904 default:
85905 {
85906 if (onMeta) {
85907 void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
85908 if (pRawData == NULL) {
85909 return DRFLAC_FALSE;
85910 }
85911 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
85912 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85913 return DRFLAC_FALSE;
85914 }
85915 metadata.pRawData = pRawData;
85916 metadata.rawDataSize = blockSize;
85917 onMeta(pUserDataMD, &metadata);
85918 drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
85919 }
85920 } break;
85921 }
85922 if (onMeta == NULL && blockSize > 0) {
85923 if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
85924 isLastBlock = DRFLAC_TRUE;
85925 }
85926 }
85927 runningFilePos += blockSize;
85928 if (isLastBlock) {
85929 break;
85930 }
85931 }
85932 *pSeektablePos = seektablePos;
85933 *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES;
85934 *pFirstFramePos = runningFilePos;
85935 return DRFLAC_TRUE;
85936 }
85937 static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
85938 {
85939 drflac_uint8 isLastBlock;
85940 drflac_uint8 blockType;
85941 drflac_uint32 blockSize;
85942 (void)onSeek;
85943 pInit->container = drflac_container_native;
85944 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
85945 return DRFLAC_FALSE;
85946 }
85947 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
85948 if (!relaxed) {
85949 return DRFLAC_FALSE;
85950 } else {
85951 pInit->hasStreamInfoBlock = DRFLAC_FALSE;
85952 pInit->hasMetadataBlocks = DRFLAC_FALSE;
85953 if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
85954 return DRFLAC_FALSE;
85955 }
85956 if (pInit->firstFrameHeader.bitsPerSample == 0) {
85957 return DRFLAC_FALSE;
85958 }
85959 pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
85960 pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
85961 pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
85962 pInit->maxBlockSizeInPCMFrames = 65535;
85963 return DRFLAC_TRUE;
85964 }
85965 } else {
85966 drflac_streaminfo streaminfo;
85967 if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
85968 return DRFLAC_FALSE;
85969 }
85970 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
85971 pInit->sampleRate = streaminfo.sampleRate;
85972 pInit->channels = streaminfo.channels;
85973 pInit->bitsPerSample = streaminfo.bitsPerSample;
85974 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
85975 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
85976 pInit->hasMetadataBlocks = !isLastBlock;
85977 if (onMeta) {
85978 drflac_metadata metadata;
85979 metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
85980 metadata.pRawData = NULL;
85981 metadata.rawDataSize = 0;
85982 metadata.data.streaminfo = streaminfo;
85983 onMeta(pUserDataMD, &metadata);
85984 }
85985 return DRFLAC_TRUE;
85986 }
85987 }
85988 #ifndef DR_FLAC_NO_OGG
85989 #define DRFLAC_OGG_MAX_PAGE_SIZE 65307
85990 #define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199
85991 typedef enum
85992 {
85993 drflac_ogg_recover_on_crc_mismatch,
85994 drflac_ogg_fail_on_crc_mismatch
85995 } drflac_ogg_crc_mismatch_recovery;
85996 #ifndef DR_FLAC_NO_CRC
85997 static drflac_uint32 drflac__crc32_table[] = {
85998 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
85999 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
86000 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
86001 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
86002 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
86003 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
86004 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
86005 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
86006 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
86007 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
86008 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
86009 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
86010 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
86011 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
86012 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
86013 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
86014 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
86015 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
86016 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
86017 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
86018 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
86019 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
86020 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
86021 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
86022 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
86023 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
86024 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
86025 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
86026 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
86027 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
86028 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
86029 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
86030 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
86031 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
86032 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
86033 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
86034 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
86035 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
86036 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
86037 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
86038 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
86039 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
86040 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
86041 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
86042 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
86043 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
86044 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
86045 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
86046 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
86047 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
86048 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
86049 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
86050 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
86051 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
86052 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
86053 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
86054 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
86055 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
86056 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
86057 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
86058 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
86059 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
86060 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
86061 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
86062 };
86063 #endif
86064 static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data)
86065 {
86066 #ifndef DR_FLAC_NO_CRC
86067 return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data];
86068 #else
86069 (void)data;
86070 return crc32;
86071 #endif
86072 }
86073 #if 0
86074 static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data)
86075 {
86076 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF));
86077 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF));
86078 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF));
86079 crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF));
86080 return crc32;
86081 }
86082 static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data)
86083 {
86084 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF));
86085 crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF));
86086 return crc32;
86087 }
86088 #endif
86089 static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize)
86090 {
86091 drflac_uint32 i;
86092 for (i = 0; i < dataSize; ++i) {
86093 crc32 = drflac_crc32_byte(crc32, pData[i]);
86094 }
86095 return crc32;
86096 }
86097 static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4])
86098 {
86099 return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
86100 }
86101 static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader)
86102 {
86103 return 27 + pHeader->segmentCount;
86104 }
86105 static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
86106 {
86107 drflac_uint32 pageBodySize = 0;
86108 int i;
86109 for (i = 0; i < pHeader->segmentCount; ++i) {
86110 pageBodySize += pHeader->segmentTable[i];
86111 }
86112 return pageBodySize;
86113 }
86114 static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
86115 {
86116 drflac_uint8 data[23];
86117 drflac_uint32 i;
86118 DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
86119 if (onRead(pUserData, data, 23) != 23) {
86120 return DRFLAC_AT_END;
86121 }
86122 *pBytesRead += 23;
86123 pHeader->capturePattern[0] = 'O';
86124 pHeader->capturePattern[1] = 'g';
86125 pHeader->capturePattern[2] = 'g';
86126 pHeader->capturePattern[3] = 'S';
86127 pHeader->structureVersion = data[0];
86128 pHeader->headerType = data[1];
86129 DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);
86130 DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4);
86131 DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4);
86132 DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4);
86133 pHeader->segmentCount = data[22];
86134 data[18] = 0;
86135 data[19] = 0;
86136 data[20] = 0;
86137 data[21] = 0;
86138 for (i = 0; i < 23; ++i) {
86139 *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]);
86140 }
86141 if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
86142 return DRFLAC_AT_END;
86143 }
86144 *pBytesRead += pHeader->segmentCount;
86145 for (i = 0; i < pHeader->segmentCount; ++i) {
86146 *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
86147 }
86148 return DRFLAC_SUCCESS;
86149 }
86150 static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
86151 {
86152 drflac_uint8 id[4];
86153 *pBytesRead = 0;
86154 if (onRead(pUserData, id, 4) != 4) {
86155 return DRFLAC_AT_END;
86156 }
86157 *pBytesRead += 4;
86158 for (;;) {
86159 if (drflac_ogg__is_capture_pattern(id)) {
86160 drflac_result result;
86161 *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
86162 result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
86163 if (result == DRFLAC_SUCCESS) {
86164 return DRFLAC_SUCCESS;
86165 } else {
86166 if (result == DRFLAC_CRC_MISMATCH) {
86167 continue;
86168 } else {
86169 return result;
86170 }
86171 }
86172 } else {
86173 id[0] = id[1];
86174 id[1] = id[2];
86175 id[2] = id[3];
86176 if (onRead(pUserData, &id[3], 1) != 1) {
86177 return DRFLAC_AT_END;
86178 }
86179 *pBytesRead += 1;
86180 }
86181 }
86182 }
86183 typedef struct
86184 {
86185 drflac_read_proc onRead;
86186 drflac_seek_proc onSeek;
86187 void* pUserData;
86188 drflac_uint64 currentBytePos;
86189 drflac_uint64 firstBytePos;
86190 drflac_uint32 serialNumber;
86191 drflac_ogg_page_header bosPageHeader;
86192 drflac_ogg_page_header currentPageHeader;
86193 drflac_uint32 bytesRemainingInPage;
86194 drflac_uint32 pageDataSize;
86195 drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE];
86196 } drflac_oggbs;
86197 static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
86198 {
86199 size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
86200 oggbs->currentBytePos += bytesActuallyRead;
86201 return bytesActuallyRead;
86202 }
86203 static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
86204 {
86205 if (origin == drflac_seek_origin_start) {
86206 if (offset <= 0x7FFFFFFF) {
86207 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
86208 return DRFLAC_FALSE;
86209 }
86210 oggbs->currentBytePos = offset;
86211 return DRFLAC_TRUE;
86212 } else {
86213 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
86214 return DRFLAC_FALSE;
86215 }
86216 oggbs->currentBytePos = offset;
86217 return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
86218 }
86219 } else {
86220 while (offset > 0x7FFFFFFF) {
86221 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
86222 return DRFLAC_FALSE;
86223 }
86224 oggbs->currentBytePos += 0x7FFFFFFF;
86225 offset -= 0x7FFFFFFF;
86226 }
86227 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) {
86228 return DRFLAC_FALSE;
86229 }
86230 oggbs->currentBytePos += offset;
86231 return DRFLAC_TRUE;
86232 }
86233 }
86234 static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod)
86235 {
86236 drflac_ogg_page_header header;
86237 for (;;) {
86238 drflac_uint32 crc32 = 0;
86239 drflac_uint32 bytesRead;
86240 drflac_uint32 pageBodySize;
86241 #ifndef DR_FLAC_NO_CRC
86242 drflac_uint32 actualCRC32;
86243 #endif
86244 if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
86245 return DRFLAC_FALSE;
86246 }
86247 oggbs->currentBytePos += bytesRead;
86248 pageBodySize = drflac_ogg__get_page_body_size(&header);
86249 if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) {
86250 continue;
86251 }
86252 if (header.serialNumber != oggbs->serialNumber) {
86253 if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
86254 return DRFLAC_FALSE;
86255 }
86256 continue;
86257 }
86258 if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
86259 return DRFLAC_FALSE;
86260 }
86261 oggbs->pageDataSize = pageBodySize;
86262 #ifndef DR_FLAC_NO_CRC
86263 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
86264 if (actualCRC32 != header.checksum) {
86265 if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) {
86266 continue;
86267 } else {
86268 drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch);
86269 return DRFLAC_FALSE;
86270 }
86271 }
86272 #else
86273 (void)recoveryMethod;
86274 #endif
86275 oggbs->currentPageHeader = header;
86276 oggbs->bytesRemainingInPage = pageBodySize;
86277 return DRFLAC_TRUE;
86278 }
86279 }
86280 #if 0
86281 static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg)
86282 {
86283 drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
86284 drflac_uint8 iSeg = 0;
86285 drflac_uint32 iByte = 0;
86286 while (iByte < bytesConsumedInPage) {
86287 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
86288 if (iByte + segmentSize > bytesConsumedInPage) {
86289 break;
86290 } else {
86291 iSeg += 1;
86292 iByte += segmentSize;
86293 }
86294 }
86295 *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte);
86296 return iSeg;
86297 }
86298 static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
86299 {
86300 for (;;) {
86301 drflac_bool32 atEndOfPage = DRFLAC_FALSE;
86302 drflac_uint8 bytesRemainingInSeg;
86303 drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
86304 drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
86305 for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
86306 drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
86307 if (segmentSize < 255) {
86308 if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
86309 atEndOfPage = DRFLAC_TRUE;
86310 }
86311 break;
86312 }
86313 bytesToEndOfPacketOrPage += segmentSize;
86314 }
86315 drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
86316 oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
86317 if (atEndOfPage) {
86318 if (!drflac_oggbs__goto_next_page(oggbs)) {
86319 return DRFLAC_FALSE;
86320 }
86321 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
86322 return DRFLAC_TRUE;
86323 }
86324 } else {
86325 return DRFLAC_TRUE;
86326 }
86327 }
86328 }
86329 static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
86330 {
86331 return drflac_oggbs__seek_to_next_packet(oggbs);
86332 }
86333 #endif
86334 static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
86335 {
86336 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
86337 drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
86338 size_t bytesRead = 0;
86339 DRFLAC_ASSERT(oggbs != NULL);
86340 DRFLAC_ASSERT(pRunningBufferOut != NULL);
86341 while (bytesRead < bytesToRead) {
86342 size_t bytesRemainingToRead = bytesToRead - bytesRead;
86343 if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
86344 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
86345 bytesRead += bytesRemainingToRead;
86346 oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead;
86347 break;
86348 }
86349 if (oggbs->bytesRemainingInPage > 0) {
86350 DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
86351 bytesRead += oggbs->bytesRemainingInPage;
86352 pRunningBufferOut += oggbs->bytesRemainingInPage;
86353 oggbs->bytesRemainingInPage = 0;
86354 }
86355 DRFLAC_ASSERT(bytesRemainingToRead > 0);
86356 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
86357 break;
86358 }
86359 }
86360 return bytesRead;
86361 }
86362 static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
86363 {
86364 drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
86365 int bytesSeeked = 0;
86366 DRFLAC_ASSERT(oggbs != NULL);
86367 DRFLAC_ASSERT(offset >= 0);
86368 if (origin == drflac_seek_origin_start) {
86369 if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
86370 return DRFLAC_FALSE;
86371 }
86372 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
86373 return DRFLAC_FALSE;
86374 }
86375 return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
86376 }
86377 DRFLAC_ASSERT(origin == drflac_seek_origin_current);
86378 while (bytesSeeked < offset) {
86379 int bytesRemainingToSeek = offset - bytesSeeked;
86380 DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
86381 if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
86382 bytesSeeked += bytesRemainingToSeek;
86383 (void)bytesSeeked;
86384 oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
86385 break;
86386 }
86387 if (oggbs->bytesRemainingInPage > 0) {
86388 bytesSeeked += (int)oggbs->bytesRemainingInPage;
86389 oggbs->bytesRemainingInPage = 0;
86390 }
86391 DRFLAC_ASSERT(bytesRemainingToSeek > 0);
86392 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
86393 return DRFLAC_FALSE;
86394 }
86395 }
86396 return DRFLAC_TRUE;
86397 }
86398 static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
86399 {
86400 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
86401 drflac_uint64 originalBytePos;
86402 drflac_uint64 runningGranulePosition;
86403 drflac_uint64 runningFrameBytePos;
86404 drflac_uint64 runningPCMFrameCount;
86405 DRFLAC_ASSERT(oggbs != NULL);
86406 originalBytePos = oggbs->currentBytePos;
86407 if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
86408 return DRFLAC_FALSE;
86409 }
86410 oggbs->bytesRemainingInPage = 0;
86411 runningGranulePosition = 0;
86412 for (;;) {
86413 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
86414 drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
86415 return DRFLAC_FALSE;
86416 }
86417 runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
86418 if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {
86419 break;
86420 }
86421 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
86422 if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
86423 drflac_uint8 firstBytesInPage[2];
86424 firstBytesInPage[0] = oggbs->pageData[0];
86425 firstBytesInPage[1] = oggbs->pageData[1];
86426 if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {
86427 runningGranulePosition = oggbs->currentPageHeader.granulePosition;
86428 }
86429 continue;
86430 }
86431 }
86432 }
86433 if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
86434 return DRFLAC_FALSE;
86435 }
86436 if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
86437 return DRFLAC_FALSE;
86438 }
86439 runningPCMFrameCount = runningGranulePosition;
86440 for (;;) {
86441 drflac_uint64 firstPCMFrameInFLACFrame = 0;
86442 drflac_uint64 lastPCMFrameInFLACFrame = 0;
86443 drflac_uint64 pcmFrameCountInThisFrame;
86444 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
86445 return DRFLAC_FALSE;
86446 }
86447 drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
86448 pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
86449 if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
86450 drflac_result result = drflac__decode_flac_frame(pFlac);
86451 if (result == DRFLAC_SUCCESS) {
86452 pFlac->currentPCMFrame = pcmFrameIndex;
86453 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
86454 return DRFLAC_TRUE;
86455 } else {
86456 return DRFLAC_FALSE;
86457 }
86458 }
86459 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
86460 drflac_result result = drflac__decode_flac_frame(pFlac);
86461 if (result == DRFLAC_SUCCESS) {
86462 drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);
86463 if (pcmFramesToDecode == 0) {
86464 return DRFLAC_TRUE;
86465 }
86466 pFlac->currentPCMFrame = runningPCMFrameCount;
86467 return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
86468 } else {
86469 if (result == DRFLAC_CRC_MISMATCH) {
86470 continue;
86471 } else {
86472 return DRFLAC_FALSE;
86473 }
86474 }
86475 } else {
86476 drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
86477 if (result == DRFLAC_SUCCESS) {
86478 runningPCMFrameCount += pcmFrameCountInThisFrame;
86479 } else {
86480 if (result == DRFLAC_CRC_MISMATCH) {
86481 continue;
86482 } else {
86483 return DRFLAC_FALSE;
86484 }
86485 }
86486 }
86487 }
86488 }
86489 static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
86490 {
86491 drflac_ogg_page_header header;
86492 drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
86493 drflac_uint32 bytesRead = 0;
86494 (void)relaxed;
86495 pInit->container = drflac_container_ogg;
86496 pInit->oggFirstBytePos = 0;
86497 if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
86498 return DRFLAC_FALSE;
86499 }
86500 pInit->runningFilePos += bytesRead;
86501 for (;;) {
86502 int pageBodySize;
86503 if ((header.headerType & 0x02) == 0) {
86504 return DRFLAC_FALSE;
86505 }
86506 pageBodySize = drflac_ogg__get_page_body_size(&header);
86507 if (pageBodySize == 51) {
86508 drflac_uint32 bytesRemainingInPage = pageBodySize;
86509 drflac_uint8 packetType;
86510 if (onRead(pUserData, &packetType, 1) != 1) {
86511 return DRFLAC_FALSE;
86512 }
86513 bytesRemainingInPage -= 1;
86514 if (packetType == 0x7F) {
86515 drflac_uint8 sig[4];
86516 if (onRead(pUserData, sig, 4) != 4) {
86517 return DRFLAC_FALSE;
86518 }
86519 bytesRemainingInPage -= 4;
86520 if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
86521 drflac_uint8 mappingVersion[2];
86522 if (onRead(pUserData, mappingVersion, 2) != 2) {
86523 return DRFLAC_FALSE;
86524 }
86525 if (mappingVersion[0] != 1) {
86526 return DRFLAC_FALSE;
86527 }
86528 if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
86529 return DRFLAC_FALSE;
86530 }
86531 if (onRead(pUserData, sig, 4) != 4) {
86532 return DRFLAC_FALSE;
86533 }
86534 if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
86535 drflac_streaminfo streaminfo;
86536 drflac_uint8 isLastBlock;
86537 drflac_uint8 blockType;
86538 drflac_uint32 blockSize;
86539 if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
86540 return DRFLAC_FALSE;
86541 }
86542 if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
86543 return DRFLAC_FALSE;
86544 }
86545 if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
86546 pInit->hasStreamInfoBlock = DRFLAC_TRUE;
86547 pInit->sampleRate = streaminfo.sampleRate;
86548 pInit->channels = streaminfo.channels;
86549 pInit->bitsPerSample = streaminfo.bitsPerSample;
86550 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
86551 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
86552 pInit->hasMetadataBlocks = !isLastBlock;
86553 if (onMeta) {
86554 drflac_metadata metadata;
86555 metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
86556 metadata.pRawData = NULL;
86557 metadata.rawDataSize = 0;
86558 metadata.data.streaminfo = streaminfo;
86559 onMeta(pUserDataMD, &metadata);
86560 }
86561 pInit->runningFilePos += pageBodySize;
86562 pInit->oggFirstBytePos = pInit->runningFilePos - 79;
86563 pInit->oggSerial = header.serialNumber;
86564 pInit->oggBosHeader = header;
86565 break;
86566 } else {
86567 return DRFLAC_FALSE;
86568 }
86569 } else {
86570 return DRFLAC_FALSE;
86571 }
86572 } else {
86573 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
86574 return DRFLAC_FALSE;
86575 }
86576 }
86577 } else {
86578 if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
86579 return DRFLAC_FALSE;
86580 }
86581 }
86582 } else {
86583 if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
86584 return DRFLAC_FALSE;
86585 }
86586 }
86587 pInit->runningFilePos += pageBodySize;
86588 if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
86589 return DRFLAC_FALSE;
86590 }
86591 pInit->runningFilePos += bytesRead;
86592 }
86593 pInit->hasMetadataBlocks = DRFLAC_TRUE;
86594 return DRFLAC_TRUE;
86595 }
86596 #endif
86597 static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
86598 {
86599 drflac_bool32 relaxed;
86600 drflac_uint8 id[4];
86601 if (pInit == NULL || onRead == NULL || onSeek == NULL) {
86602 return DRFLAC_FALSE;
86603 }
86604 DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
86605 pInit->onRead = onRead;
86606 pInit->onSeek = onSeek;
86607 pInit->onMeta = onMeta;
86608 pInit->container = container;
86609 pInit->pUserData = pUserData;
86610 pInit->pUserDataMD = pUserDataMD;
86611 pInit->bs.onRead = onRead;
86612 pInit->bs.onSeek = onSeek;
86613 pInit->bs.pUserData = pUserData;
86614 drflac__reset_cache(&pInit->bs);
86615 relaxed = container != drflac_container_unknown;
86616 for (;;) {
86617 if (onRead(pUserData, id, 4) != 4) {
86618 return DRFLAC_FALSE;
86619 }
86620 pInit->runningFilePos += 4;
86621 if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
86622 drflac_uint8 header[6];
86623 drflac_uint8 flags;
86624 drflac_uint32 headerSize;
86625 if (onRead(pUserData, header, 6) != 6) {
86626 return DRFLAC_FALSE;
86627 }
86628 pInit->runningFilePos += 6;
86629 flags = header[1];
86630 DRFLAC_COPY_MEMORY(&headerSize, header+2, 4);
86631 headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize));
86632 if (flags & 0x10) {
86633 headerSize += 10;
86634 }
86635 if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
86636 return DRFLAC_FALSE;
86637 }
86638 pInit->runningFilePos += headerSize;
86639 } else {
86640 break;
86641 }
86642 }
86643 if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
86644 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86645 }
86646 #ifndef DR_FLAC_NO_OGG
86647 if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
86648 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86649 }
86650 #endif
86651 if (relaxed) {
86652 if (container == drflac_container_native) {
86653 return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86654 }
86655 #ifndef DR_FLAC_NO_OGG
86656 if (container == drflac_container_ogg) {
86657 return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
86658 }
86659 #endif
86660 }
86661 return DRFLAC_FALSE;
86662 }
86663 static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit)
86664 {
86665 DRFLAC_ASSERT(pFlac != NULL);
86666 DRFLAC_ASSERT(pInit != NULL);
86667 DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));
86668 pFlac->bs = pInit->bs;
86669 pFlac->onMeta = pInit->onMeta;
86670 pFlac->pUserDataMD = pInit->pUserDataMD;
86671 pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
86672 pFlac->sampleRate = pInit->sampleRate;
86673 pFlac->channels = (drflac_uint8)pInit->channels;
86674 pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
86675 pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
86676 pFlac->container = pInit->container;
86677 }
86678 static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks)
86679 {
86680 drflac_init_info init;
86681 drflac_uint32 allocationSize;
86682 drflac_uint32 wholeSIMDVectorCountPerChannel;
86683 drflac_uint32 decodedSamplesAllocationSize;
86684 #ifndef DR_FLAC_NO_OGG
86685 drflac_oggbs* pOggbs = NULL;
86686 #endif
86687 drflac_uint64 firstFramePos;
86688 drflac_uint64 seektablePos;
86689 drflac_uint32 seekpointCount;
86690 drflac_allocation_callbacks allocationCallbacks;
86691 drflac* pFlac;
86692 drflac__init_cpu_caps();
86693 if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
86694 return NULL;
86695 }
86696 if (pAllocationCallbacks != NULL) {
86697 allocationCallbacks = *pAllocationCallbacks;
86698 if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
86699 return NULL;
86700 }
86701 } else {
86702 allocationCallbacks.pUserData = NULL;
86703 allocationCallbacks.onMalloc = drflac__malloc_default;
86704 allocationCallbacks.onRealloc = drflac__realloc_default;
86705 allocationCallbacks.onFree = drflac__free_default;
86706 }
86707 allocationSize = sizeof(drflac);
86708 if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
86709 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
86710 } else {
86711 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
86712 }
86713 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
86714 allocationSize += decodedSamplesAllocationSize;
86715 allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE;
86716 #ifndef DR_FLAC_NO_OGG
86717 if (init.container == drflac_container_ogg) {
86718 allocationSize += sizeof(drflac_oggbs);
86719 pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks);
86720 if (pOggbs == NULL) {
86721 return NULL;
86722 }
86723 DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs));
86724 pOggbs->onRead = onRead;
86725 pOggbs->onSeek = onSeek;
86726 pOggbs->pUserData = pUserData;
86727 pOggbs->currentBytePos = init.oggFirstBytePos;
86728 pOggbs->firstBytePos = init.oggFirstBytePos;
86729 pOggbs->serialNumber = init.oggSerial;
86730 pOggbs->bosPageHeader = init.oggBosHeader;
86731 pOggbs->bytesRemainingInPage = 0;
86732 }
86733 #endif
86734 firstFramePos = 42;
86735 seektablePos = 0;
86736 seekpointCount = 0;
86737 if (init.hasMetadataBlocks) {
86738 drflac_read_proc onReadOverride = onRead;
86739 drflac_seek_proc onSeekOverride = onSeek;
86740 void* pUserDataOverride = pUserData;
86741 #ifndef DR_FLAC_NO_OGG
86742 if (init.container == drflac_container_ogg) {
86743 onReadOverride = drflac__on_read_ogg;
86744 onSeekOverride = drflac__on_seek_ogg;
86745 pUserDataOverride = (void*)pOggbs;
86746 }
86747 #endif
86748 if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
86749 #ifndef DR_FLAC_NO_OGG
86750 drflac__free_from_callbacks(pOggbs, &allocationCallbacks);
86751 #endif
86752 return NULL;
86753 }
86754 allocationSize += seekpointCount * sizeof(drflac_seekpoint);
86755 }
86756 pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
86757 if (pFlac == NULL) {
86758 #ifndef DR_FLAC_NO_OGG
86759 drflac__free_from_callbacks(pOggbs, &allocationCallbacks);
86760 #endif
86761 return NULL;
86762 }
86763 drflac__init_from_info(pFlac, &init);
86764 pFlac->allocationCallbacks = allocationCallbacks;
86765 pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
86766 #ifndef DR_FLAC_NO_OGG
86767 if (init.container == drflac_container_ogg) {
86768 drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint)));
86769 DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs));
86770 drflac__free_from_callbacks(pOggbs, &allocationCallbacks);
86771 pOggbs = NULL;
86772 pFlac->bs.onRead = drflac__on_read_ogg;
86773 pFlac->bs.onSeek = drflac__on_seek_ogg;
86774 pFlac->bs.pUserData = (void*)pInternalOggbs;
86775 pFlac->_oggbs = (void*)pInternalOggbs;
86776 }
86777 #endif
86778 pFlac->firstFLACFramePosInBytes = firstFramePos;
86779 #ifndef DR_FLAC_NO_OGG
86780 if (init.container == drflac_container_ogg)
86781 {
86782 pFlac->pSeekpoints = NULL;
86783 pFlac->seekpointCount = 0;
86784 }
86785 else
86786 #endif
86787 {
86788 if (seektablePos != 0) {
86789 pFlac->seekpointCount = seekpointCount;
86790 pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
86791 DRFLAC_ASSERT(pFlac->bs.onSeek != NULL);
86792 DRFLAC_ASSERT(pFlac->bs.onRead != NULL);
86793 if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
86794 drflac_uint32 iSeekpoint;
86795 for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) {
86796 if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) {
86797 pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
86798 pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
86799 pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
86800 } else {
86801 pFlac->pSeekpoints = NULL;
86802 pFlac->seekpointCount = 0;
86803 break;
86804 }
86805 }
86806 if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) {
86807 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
86808 return NULL;
86809 }
86810 } else {
86811 pFlac->pSeekpoints = NULL;
86812 pFlac->seekpointCount = 0;
86813 }
86814 }
86815 }
86816 if (!init.hasStreamInfoBlock) {
86817 pFlac->currentFLACFrame.header = init.firstFrameHeader;
86818 for (;;) {
86819 drflac_result result = drflac__decode_flac_frame(pFlac);
86820 if (result == DRFLAC_SUCCESS) {
86821 break;
86822 } else {
86823 if (result == DRFLAC_CRC_MISMATCH) {
86824 if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
86825 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
86826 return NULL;
86827 }
86828 continue;
86829 } else {
86830 drflac__free_from_callbacks(pFlac, &allocationCallbacks);
86831 return NULL;
86832 }
86833 }
86834 }
86835 }
86836 return pFlac;
86837 }
86838 #ifndef DR_FLAC_NO_STDIO
86839 #include <stdio.h>
86840 #ifndef DR_FLAC_NO_WCHAR
86841 #include <wchar.h>
86842 #endif
86843 #include <errno.h>
86844 static drflac_result drflac_result_from_errno(int e)
86845 {
86846 switch (e)
86847 {
86848 case 0: return DRFLAC_SUCCESS;
86849 #ifdef EPERM
86850 case EPERM: return DRFLAC_INVALID_OPERATION;
86851 #endif
86852 #ifdef ENOENT
86853 case ENOENT: return DRFLAC_DOES_NOT_EXIST;
86854 #endif
86855 #ifdef ESRCH
86856 case ESRCH: return DRFLAC_DOES_NOT_EXIST;
86857 #endif
86858 #ifdef EINTR
86859 case EINTR: return DRFLAC_INTERRUPT;
86860 #endif
86861 #ifdef EIO
86862 case EIO: return DRFLAC_IO_ERROR;
86863 #endif
86864 #ifdef ENXIO
86865 case ENXIO: return DRFLAC_DOES_NOT_EXIST;
86866 #endif
86867 #ifdef E2BIG
86868 case E2BIG: return DRFLAC_INVALID_ARGS;
86869 #endif
86870 #ifdef ENOEXEC
86871 case ENOEXEC: return DRFLAC_INVALID_FILE;
86872 #endif
86873 #ifdef EBADF
86874 case EBADF: return DRFLAC_INVALID_FILE;
86875 #endif
86876 #ifdef ECHILD
86877 case ECHILD: return DRFLAC_ERROR;
86878 #endif
86879 #ifdef EAGAIN
86880 case EAGAIN: return DRFLAC_UNAVAILABLE;
86881 #endif
86882 #ifdef ENOMEM
86883 case ENOMEM: return DRFLAC_OUT_OF_MEMORY;
86884 #endif
86885 #ifdef EACCES
86886 case EACCES: return DRFLAC_ACCESS_DENIED;
86887 #endif
86888 #ifdef EFAULT
86889 case EFAULT: return DRFLAC_BAD_ADDRESS;
86890 #endif
86891 #ifdef ENOTBLK
86892 case ENOTBLK: return DRFLAC_ERROR;
86893 #endif
86894 #ifdef EBUSY
86895 case EBUSY: return DRFLAC_BUSY;
86896 #endif
86897 #ifdef EEXIST
86898 case EEXIST: return DRFLAC_ALREADY_EXISTS;
86899 #endif
86900 #ifdef EXDEV
86901 case EXDEV: return DRFLAC_ERROR;
86902 #endif
86903 #ifdef ENODEV
86904 case ENODEV: return DRFLAC_DOES_NOT_EXIST;
86905 #endif
86906 #ifdef ENOTDIR
86907 case ENOTDIR: return DRFLAC_NOT_DIRECTORY;
86908 #endif
86909 #ifdef EISDIR
86910 case EISDIR: return DRFLAC_IS_DIRECTORY;
86911 #endif
86912 #ifdef EINVAL
86913 case EINVAL: return DRFLAC_INVALID_ARGS;
86914 #endif
86915 #ifdef ENFILE
86916 case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
86917 #endif
86918 #ifdef EMFILE
86919 case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES;
86920 #endif
86921 #ifdef ENOTTY
86922 case ENOTTY: return DRFLAC_INVALID_OPERATION;
86923 #endif
86924 #ifdef ETXTBSY
86925 case ETXTBSY: return DRFLAC_BUSY;
86926 #endif
86927 #ifdef EFBIG
86928 case EFBIG: return DRFLAC_TOO_BIG;
86929 #endif
86930 #ifdef ENOSPC
86931 case ENOSPC: return DRFLAC_NO_SPACE;
86932 #endif
86933 #ifdef ESPIPE
86934 case ESPIPE: return DRFLAC_BAD_SEEK;
86935 #endif
86936 #ifdef EROFS
86937 case EROFS: return DRFLAC_ACCESS_DENIED;
86938 #endif
86939 #ifdef EMLINK
86940 case EMLINK: return DRFLAC_TOO_MANY_LINKS;
86941 #endif
86942 #ifdef EPIPE
86943 case EPIPE: return DRFLAC_BAD_PIPE;
86944 #endif
86945 #ifdef EDOM
86946 case EDOM: return DRFLAC_OUT_OF_RANGE;
86947 #endif
86948 #ifdef ERANGE
86949 case ERANGE: return DRFLAC_OUT_OF_RANGE;
86950 #endif
86951 #ifdef EDEADLK
86952 case EDEADLK: return DRFLAC_DEADLOCK;
86953 #endif
86954 #ifdef ENAMETOOLONG
86955 case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG;
86956 #endif
86957 #ifdef ENOLCK
86958 case ENOLCK: return DRFLAC_ERROR;
86959 #endif
86960 #ifdef ENOSYS
86961 case ENOSYS: return DRFLAC_NOT_IMPLEMENTED;
86962 #endif
86963 #ifdef ENOTEMPTY
86964 case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY;
86965 #endif
86966 #ifdef ELOOP
86967 case ELOOP: return DRFLAC_TOO_MANY_LINKS;
86968 #endif
86969 #ifdef ENOMSG
86970 case ENOMSG: return DRFLAC_NO_MESSAGE;
86971 #endif
86972 #ifdef EIDRM
86973 case EIDRM: return DRFLAC_ERROR;
86974 #endif
86975 #ifdef ECHRNG
86976 case ECHRNG: return DRFLAC_ERROR;
86977 #endif
86978 #ifdef EL2NSYNC
86979 case EL2NSYNC: return DRFLAC_ERROR;
86980 #endif
86981 #ifdef EL3HLT
86982 case EL3HLT: return DRFLAC_ERROR;
86983 #endif
86984 #ifdef EL3RST
86985 case EL3RST: return DRFLAC_ERROR;
86986 #endif
86987 #ifdef ELNRNG
86988 case ELNRNG: return DRFLAC_OUT_OF_RANGE;
86989 #endif
86990 #ifdef EUNATCH
86991 case EUNATCH: return DRFLAC_ERROR;
86992 #endif
86993 #ifdef ENOCSI
86994 case ENOCSI: return DRFLAC_ERROR;
86995 #endif
86996 #ifdef EL2HLT
86997 case EL2HLT: return DRFLAC_ERROR;
86998 #endif
86999 #ifdef EBADE
87000 case EBADE: return DRFLAC_ERROR;
87001 #endif
87002 #ifdef EBADR
87003 case EBADR: return DRFLAC_ERROR;
87004 #endif
87005 #ifdef EXFULL
87006 case EXFULL: return DRFLAC_ERROR;
87007 #endif
87008 #ifdef ENOANO
87009 case ENOANO: return DRFLAC_ERROR;
87010 #endif
87011 #ifdef EBADRQC
87012 case EBADRQC: return DRFLAC_ERROR;
87013 #endif
87014 #ifdef EBADSLT
87015 case EBADSLT: return DRFLAC_ERROR;
87016 #endif
87017 #ifdef EBFONT
87018 case EBFONT: return DRFLAC_INVALID_FILE;
87019 #endif
87020 #ifdef ENOSTR
87021 case ENOSTR: return DRFLAC_ERROR;
87022 #endif
87023 #ifdef ENODATA
87024 case ENODATA: return DRFLAC_NO_DATA_AVAILABLE;
87025 #endif
87026 #ifdef ETIME
87027 case ETIME: return DRFLAC_TIMEOUT;
87028 #endif
87029 #ifdef ENOSR
87030 case ENOSR: return DRFLAC_NO_DATA_AVAILABLE;
87031 #endif
87032 #ifdef ENONET
87033 case ENONET: return DRFLAC_NO_NETWORK;
87034 #endif
87035 #ifdef ENOPKG
87036 case ENOPKG: return DRFLAC_ERROR;
87037 #endif
87038 #ifdef EREMOTE
87039 case EREMOTE: return DRFLAC_ERROR;
87040 #endif
87041 #ifdef ENOLINK
87042 case ENOLINK: return DRFLAC_ERROR;
87043 #endif
87044 #ifdef EADV
87045 case EADV: return DRFLAC_ERROR;
87046 #endif
87047 #ifdef ESRMNT
87048 case ESRMNT: return DRFLAC_ERROR;
87049 #endif
87050 #ifdef ECOMM
87051 case ECOMM: return DRFLAC_ERROR;
87052 #endif
87053 #ifdef EPROTO
87054 case EPROTO: return DRFLAC_ERROR;
87055 #endif
87056 #ifdef EMULTIHOP
87057 case EMULTIHOP: return DRFLAC_ERROR;
87058 #endif
87059 #ifdef EDOTDOT
87060 case EDOTDOT: return DRFLAC_ERROR;
87061 #endif
87062 #ifdef EBADMSG
87063 case EBADMSG: return DRFLAC_BAD_MESSAGE;
87064 #endif
87065 #ifdef EOVERFLOW
87066 case EOVERFLOW: return DRFLAC_TOO_BIG;
87067 #endif
87068 #ifdef ENOTUNIQ
87069 case ENOTUNIQ: return DRFLAC_NOT_UNIQUE;
87070 #endif
87071 #ifdef EBADFD
87072 case EBADFD: return DRFLAC_ERROR;
87073 #endif
87074 #ifdef EREMCHG
87075 case EREMCHG: return DRFLAC_ERROR;
87076 #endif
87077 #ifdef ELIBACC
87078 case ELIBACC: return DRFLAC_ACCESS_DENIED;
87079 #endif
87080 #ifdef ELIBBAD
87081 case ELIBBAD: return DRFLAC_INVALID_FILE;
87082 #endif
87083 #ifdef ELIBSCN
87084 case ELIBSCN: return DRFLAC_INVALID_FILE;
87085 #endif
87086 #ifdef ELIBMAX
87087 case ELIBMAX: return DRFLAC_ERROR;
87088 #endif
87089 #ifdef ELIBEXEC
87090 case ELIBEXEC: return DRFLAC_ERROR;
87091 #endif
87092 #ifdef EILSEQ
87093 case EILSEQ: return DRFLAC_INVALID_DATA;
87094 #endif
87095 #ifdef ERESTART
87096 case ERESTART: return DRFLAC_ERROR;
87097 #endif
87098 #ifdef ESTRPIPE
87099 case ESTRPIPE: return DRFLAC_ERROR;
87100 #endif
87101 #ifdef EUSERS
87102 case EUSERS: return DRFLAC_ERROR;
87103 #endif
87104 #ifdef ENOTSOCK
87105 case ENOTSOCK: return DRFLAC_NOT_SOCKET;
87106 #endif
87107 #ifdef EDESTADDRREQ
87108 case EDESTADDRREQ: return DRFLAC_NO_ADDRESS;
87109 #endif
87110 #ifdef EMSGSIZE
87111 case EMSGSIZE: return DRFLAC_TOO_BIG;
87112 #endif
87113 #ifdef EPROTOTYPE
87114 case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL;
87115 #endif
87116 #ifdef ENOPROTOOPT
87117 case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE;
87118 #endif
87119 #ifdef EPROTONOSUPPORT
87120 case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED;
87121 #endif
87122 #ifdef ESOCKTNOSUPPORT
87123 case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED;
87124 #endif
87125 #ifdef EOPNOTSUPP
87126 case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION;
87127 #endif
87128 #ifdef EPFNOSUPPORT
87129 case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED;
87130 #endif
87131 #ifdef EAFNOSUPPORT
87132 case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED;
87133 #endif
87134 #ifdef EADDRINUSE
87135 case EADDRINUSE: return DRFLAC_ALREADY_IN_USE;
87136 #endif
87137 #ifdef EADDRNOTAVAIL
87138 case EADDRNOTAVAIL: return DRFLAC_ERROR;
87139 #endif
87140 #ifdef ENETDOWN
87141 case ENETDOWN: return DRFLAC_NO_NETWORK;
87142 #endif
87143 #ifdef ENETUNREACH
87144 case ENETUNREACH: return DRFLAC_NO_NETWORK;
87145 #endif
87146 #ifdef ENETRESET
87147 case ENETRESET: return DRFLAC_NO_NETWORK;
87148 #endif
87149 #ifdef ECONNABORTED
87150 case ECONNABORTED: return DRFLAC_NO_NETWORK;
87151 #endif
87152 #ifdef ECONNRESET
87153 case ECONNRESET: return DRFLAC_CONNECTION_RESET;
87154 #endif
87155 #ifdef ENOBUFS
87156 case ENOBUFS: return DRFLAC_NO_SPACE;
87157 #endif
87158 #ifdef EISCONN
87159 case EISCONN: return DRFLAC_ALREADY_CONNECTED;
87160 #endif
87161 #ifdef ENOTCONN
87162 case ENOTCONN: return DRFLAC_NOT_CONNECTED;
87163 #endif
87164 #ifdef ESHUTDOWN
87165 case ESHUTDOWN: return DRFLAC_ERROR;
87166 #endif
87167 #ifdef ETOOMANYREFS
87168 case ETOOMANYREFS: return DRFLAC_ERROR;
87169 #endif
87170 #ifdef ETIMEDOUT
87171 case ETIMEDOUT: return DRFLAC_TIMEOUT;
87172 #endif
87173 #ifdef ECONNREFUSED
87174 case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED;
87175 #endif
87176 #ifdef EHOSTDOWN
87177 case EHOSTDOWN: return DRFLAC_NO_HOST;
87178 #endif
87179 #ifdef EHOSTUNREACH
87180 case EHOSTUNREACH: return DRFLAC_NO_HOST;
87181 #endif
87182 #ifdef EALREADY
87183 case EALREADY: return DRFLAC_IN_PROGRESS;
87184 #endif
87185 #ifdef EINPROGRESS
87186 case EINPROGRESS: return DRFLAC_IN_PROGRESS;
87187 #endif
87188 #ifdef ESTALE
87189 case ESTALE: return DRFLAC_INVALID_FILE;
87190 #endif
87191 #ifdef EUCLEAN
87192 case EUCLEAN: return DRFLAC_ERROR;
87193 #endif
87194 #ifdef ENOTNAM
87195 case ENOTNAM: return DRFLAC_ERROR;
87196 #endif
87197 #ifdef ENAVAIL
87198 case ENAVAIL: return DRFLAC_ERROR;
87199 #endif
87200 #ifdef EISNAM
87201 case EISNAM: return DRFLAC_ERROR;
87202 #endif
87203 #ifdef EREMOTEIO
87204 case EREMOTEIO: return DRFLAC_IO_ERROR;
87205 #endif
87206 #ifdef EDQUOT
87207 case EDQUOT: return DRFLAC_NO_SPACE;
87208 #endif
87209 #ifdef ENOMEDIUM
87210 case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST;
87211 #endif
87212 #ifdef EMEDIUMTYPE
87213 case EMEDIUMTYPE: return DRFLAC_ERROR;
87214 #endif
87215 #ifdef ECANCELED
87216 case ECANCELED: return DRFLAC_CANCELLED;
87217 #endif
87218 #ifdef ENOKEY
87219 case ENOKEY: return DRFLAC_ERROR;
87220 #endif
87221 #ifdef EKEYEXPIRED
87222 case EKEYEXPIRED: return DRFLAC_ERROR;
87223 #endif
87224 #ifdef EKEYREVOKED
87225 case EKEYREVOKED: return DRFLAC_ERROR;
87226 #endif
87227 #ifdef EKEYREJECTED
87228 case EKEYREJECTED: return DRFLAC_ERROR;
87229 #endif
87230 #ifdef EOWNERDEAD
87231 case EOWNERDEAD: return DRFLAC_ERROR;
87232 #endif
87233 #ifdef ENOTRECOVERABLE
87234 case ENOTRECOVERABLE: return DRFLAC_ERROR;
87235 #endif
87236 #ifdef ERFKILL
87237 case ERFKILL: return DRFLAC_ERROR;
87238 #endif
87239 #ifdef EHWPOISON
87240 case EHWPOISON: return DRFLAC_ERROR;
87241 #endif
87242 default: return DRFLAC_ERROR;
87243 }
87244 }
87245 static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
87246 {
87247 #if defined(_MSC_VER) && _MSC_VER >= 1400
87248 errno_t err;
87249 #endif
87250 if (ppFile != NULL) {
87251 *ppFile = NULL;
87252 }
87253 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
87254 return DRFLAC_INVALID_ARGS;
87255 }
87256 #if defined(_MSC_VER) && _MSC_VER >= 1400
87257 err = fopen_s(ppFile, pFilePath, pOpenMode);
87258 if (err != 0) {
87259 return drflac_result_from_errno(err);
87260 }
87261 #else
87262 #if defined(_WIN32) || defined(__APPLE__)
87263 *ppFile = fopen(pFilePath, pOpenMode);
87264 #else
87265 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
87266 *ppFile = fopen64(pFilePath, pOpenMode);
87267 #else
87268 *ppFile = fopen(pFilePath, pOpenMode);
87269 #endif
87270 #endif
87271 if (*ppFile == NULL) {
87272 drflac_result result = drflac_result_from_errno(errno);
87273 if (result == DRFLAC_SUCCESS) {
87274 result = DRFLAC_ERROR;
87275 }
87276 return result;
87277 }
87278 #endif
87279 return DRFLAC_SUCCESS;
87280 }
87281 #if defined(_WIN32)
87282 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
87283 #define DRFLAC_HAS_WFOPEN
87284 #endif
87285 #endif
87286 #ifndef DR_FLAC_NO_WCHAR
87287 static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks)
87288 {
87289 if (ppFile != NULL) {
87290 *ppFile = NULL;
87291 }
87292 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
87293 return DRFLAC_INVALID_ARGS;
87294 }
87295 #if defined(DRFLAC_HAS_WFOPEN)
87296 {
87297 #if defined(_MSC_VER) && _MSC_VER >= 1400
87298 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
87299 if (err != 0) {
87300 return drflac_result_from_errno(err);
87301 }
87302 #else
87303 *ppFile = _wfopen(pFilePath, pOpenMode);
87304 if (*ppFile == NULL) {
87305 return drflac_result_from_errno(errno);
87306 }
87307 #endif
87308 (void)pAllocationCallbacks;
87309 }
87310 #else
87311 #if defined(__DJGPP__)
87312 {
87313 }
87314 #else
87315 {
87316 mbstate_t mbs;
87317 size_t lenMB;
87318 const wchar_t* pFilePathTemp = pFilePath;
87319 char* pFilePathMB = NULL;
87320 char pOpenModeMB[32] = {0};
87321 DRFLAC_ZERO_OBJECT(&mbs);
87322 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
87323 if (lenMB == (size_t)-1) {
87324 return drflac_result_from_errno(errno);
87325 }
87326 pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
87327 if (pFilePathMB == NULL) {
87328 return DRFLAC_OUT_OF_MEMORY;
87329 }
87330 pFilePathTemp = pFilePath;
87331 DRFLAC_ZERO_OBJECT(&mbs);
87332 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
87333 {
87334 size_t i = 0;
87335 for (;;) {
87336 if (pOpenMode[i] == 0) {
87337 pOpenModeMB[i] = '\0';
87338 break;
87339 }
87340 pOpenModeMB[i] = (char)pOpenMode[i];
87341 i += 1;
87342 }
87343 }
87344 *ppFile = fopen(pFilePathMB, pOpenModeMB);
87345 drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
87346 }
87347 #endif
87348 if (*ppFile == NULL) {
87349 return DRFLAC_ERROR;
87350 }
87351 #endif
87352 return DRFLAC_SUCCESS;
87353 }
87354 #endif
87355 static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
87356 {
87357 return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
87358 }
87359 static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
87360 {
87361 DRFLAC_ASSERT(offset >= 0);
87362 return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
87363 }
87364 DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
87365 {
87366 drflac* pFlac;
87367 FILE* pFile;
87368 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
87369 return NULL;
87370 }
87371 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
87372 if (pFlac == NULL) {
87373 fclose(pFile);
87374 return NULL;
87375 }
87376 return pFlac;
87377 }
87378 #ifndef DR_FLAC_NO_WCHAR
87379 DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
87380 {
87381 drflac* pFlac;
87382 FILE* pFile;
87383 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
87384 return NULL;
87385 }
87386 pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
87387 if (pFlac == NULL) {
87388 fclose(pFile);
87389 return NULL;
87390 }
87391 return pFlac;
87392 }
87393 #endif
87394 DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
87395 {
87396 drflac* pFlac;
87397 FILE* pFile;
87398 if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) {
87399 return NULL;
87400 }
87401 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
87402 if (pFlac == NULL) {
87403 fclose(pFile);
87404 return pFlac;
87405 }
87406 return pFlac;
87407 }
87408 #ifndef DR_FLAC_NO_WCHAR
87409 DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
87410 {
87411 drflac* pFlac;
87412 FILE* pFile;
87413 if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) {
87414 return NULL;
87415 }
87416 pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
87417 if (pFlac == NULL) {
87418 fclose(pFile);
87419 return pFlac;
87420 }
87421 return pFlac;
87422 }
87423 #endif
87424 #endif
87425 static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
87426 {
87427 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
87428 size_t bytesRemaining;
87429 DRFLAC_ASSERT(memoryStream != NULL);
87430 DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
87431 bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
87432 if (bytesToRead > bytesRemaining) {
87433 bytesToRead = bytesRemaining;
87434 }
87435 if (bytesToRead > 0) {
87436 DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
87437 memoryStream->currentReadPos += bytesToRead;
87438 }
87439 return bytesToRead;
87440 }
87441 static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
87442 {
87443 drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
87444 DRFLAC_ASSERT(memoryStream != NULL);
87445 DRFLAC_ASSERT(offset >= 0);
87446 if (offset > (drflac_int64)memoryStream->dataSize) {
87447 return DRFLAC_FALSE;
87448 }
87449 if (origin == drflac_seek_origin_current) {
87450 if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
87451 memoryStream->currentReadPos += offset;
87452 } else {
87453 return DRFLAC_FALSE;
87454 }
87455 } else {
87456 if ((drflac_uint32)offset <= memoryStream->dataSize) {
87457 memoryStream->currentReadPos = offset;
87458 } else {
87459 return DRFLAC_FALSE;
87460 }
87461 }
87462 return DRFLAC_TRUE;
87463 }
87464 DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks)
87465 {
87466 drflac__memory_stream memoryStream;
87467 drflac* pFlac;
87468 memoryStream.data = (const drflac_uint8*)pData;
87469 memoryStream.dataSize = dataSize;
87470 memoryStream.currentReadPos = 0;
87471 pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
87472 if (pFlac == NULL) {
87473 return NULL;
87474 }
87475 pFlac->memoryStream = memoryStream;
87476 #ifndef DR_FLAC_NO_OGG
87477 if (pFlac->container == drflac_container_ogg)
87478 {
87479 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
87480 oggbs->pUserData = &pFlac->memoryStream;
87481 }
87482 else
87483 #endif
87484 {
87485 pFlac->bs.pUserData = &pFlac->memoryStream;
87486 }
87487 return pFlac;
87488 }
87489 DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
87490 {
87491 drflac__memory_stream memoryStream;
87492 drflac* pFlac;
87493 memoryStream.data = (const drflac_uint8*)pData;
87494 memoryStream.dataSize = dataSize;
87495 memoryStream.currentReadPos = 0;
87496 pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
87497 if (pFlac == NULL) {
87498 return NULL;
87499 }
87500 pFlac->memoryStream = memoryStream;
87501 #ifndef DR_FLAC_NO_OGG
87502 if (pFlac->container == drflac_container_ogg)
87503 {
87504 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
87505 oggbs->pUserData = &pFlac->memoryStream;
87506 }
87507 else
87508 #endif
87509 {
87510 pFlac->bs.pUserData = &pFlac->memoryStream;
87511 }
87512 return pFlac;
87513 }
87514 DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
87515 {
87516 return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
87517 }
87518 DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
87519 {
87520 return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
87521 }
87522 DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
87523 {
87524 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
87525 }
87526 DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
87527 {
87528 return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
87529 }
87530 DRFLAC_API void drflac_close(drflac* pFlac)
87531 {
87532 if (pFlac == NULL) {
87533 return;
87534 }
87535 #ifndef DR_FLAC_NO_STDIO
87536 if (pFlac->bs.onRead == drflac__on_read_stdio) {
87537 fclose((FILE*)pFlac->bs.pUserData);
87538 }
87539 #ifndef DR_FLAC_NO_OGG
87540 if (pFlac->container == drflac_container_ogg) {
87541 drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
87542 DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg);
87543 if (oggbs->onRead == drflac__on_read_stdio) {
87544 fclose((FILE*)oggbs->pUserData);
87545 }
87546 }
87547 #endif
87548 #endif
87549 drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
87550 }
87551 #if 0
87552 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87553 {
87554 drflac_uint64 i;
87555 for (i = 0; i < frameCount; ++i) {
87556 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87557 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87558 drflac_uint32 right = left - side;
87559 pOutputSamples[i*2+0] = (drflac_int32)left;
87560 pOutputSamples[i*2+1] = (drflac_int32)right;
87561 }
87562 }
87563 #endif
87564 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87565 {
87566 drflac_uint64 i;
87567 drflac_uint64 frameCount4 = frameCount >> 2;
87568 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87569 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87570 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87571 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87572 for (i = 0; i < frameCount4; ++i) {
87573 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
87574 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
87575 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
87576 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
87577 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
87578 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
87579 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
87580 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
87581 drflac_uint32 right0 = left0 - side0;
87582 drflac_uint32 right1 = left1 - side1;
87583 drflac_uint32 right2 = left2 - side2;
87584 drflac_uint32 right3 = left3 - side3;
87585 pOutputSamples[i*8+0] = (drflac_int32)left0;
87586 pOutputSamples[i*8+1] = (drflac_int32)right0;
87587 pOutputSamples[i*8+2] = (drflac_int32)left1;
87588 pOutputSamples[i*8+3] = (drflac_int32)right1;
87589 pOutputSamples[i*8+4] = (drflac_int32)left2;
87590 pOutputSamples[i*8+5] = (drflac_int32)right2;
87591 pOutputSamples[i*8+6] = (drflac_int32)left3;
87592 pOutputSamples[i*8+7] = (drflac_int32)right3;
87593 }
87594 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87595 drflac_uint32 left = pInputSamples0U32[i] << shift0;
87596 drflac_uint32 side = pInputSamples1U32[i] << shift1;
87597 drflac_uint32 right = left - side;
87598 pOutputSamples[i*2+0] = (drflac_int32)left;
87599 pOutputSamples[i*2+1] = (drflac_int32)right;
87600 }
87601 }
87602 #if defined(DRFLAC_SUPPORT_SSE2)
87603 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87604 {
87605 drflac_uint64 i;
87606 drflac_uint64 frameCount4 = frameCount >> 2;
87607 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87608 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87609 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87610 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87611 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
87612 for (i = 0; i < frameCount4; ++i) {
87613 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
87614 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
87615 __m128i right = _mm_sub_epi32(left, side);
87616 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87617 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87618 }
87619 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87620 drflac_uint32 left = pInputSamples0U32[i] << shift0;
87621 drflac_uint32 side = pInputSamples1U32[i] << shift1;
87622 drflac_uint32 right = left - side;
87623 pOutputSamples[i*2+0] = (drflac_int32)left;
87624 pOutputSamples[i*2+1] = (drflac_int32)right;
87625 }
87626 }
87627 #endif
87628 #if defined(DRFLAC_SUPPORT_NEON)
87629 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87630 {
87631 drflac_uint64 i;
87632 drflac_uint64 frameCount4 = frameCount >> 2;
87633 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87634 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87635 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87636 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87637 int32x4_t shift0_4;
87638 int32x4_t shift1_4;
87639 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
87640 shift0_4 = vdupq_n_s32(shift0);
87641 shift1_4 = vdupq_n_s32(shift1);
87642 for (i = 0; i < frameCount4; ++i) {
87643 uint32x4_t left;
87644 uint32x4_t side;
87645 uint32x4_t right;
87646 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
87647 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
87648 right = vsubq_u32(left, side);
87649 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
87650 }
87651 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87652 drflac_uint32 left = pInputSamples0U32[i] << shift0;
87653 drflac_uint32 side = pInputSamples1U32[i] << shift1;
87654 drflac_uint32 right = left - side;
87655 pOutputSamples[i*2+0] = (drflac_int32)left;
87656 pOutputSamples[i*2+1] = (drflac_int32)right;
87657 }
87658 }
87659 #endif
87660 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87661 {
87662 #if defined(DRFLAC_SUPPORT_SSE2)
87663 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
87664 drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87665 } else
87666 #elif defined(DRFLAC_SUPPORT_NEON)
87667 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
87668 drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87669 } else
87670 #endif
87671 {
87672 #if 0
87673 drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87674 #else
87675 drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87676 #endif
87677 }
87678 }
87679 #if 0
87680 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87681 {
87682 drflac_uint64 i;
87683 for (i = 0; i < frameCount; ++i) {
87684 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87685 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87686 drflac_uint32 left = right + side;
87687 pOutputSamples[i*2+0] = (drflac_int32)left;
87688 pOutputSamples[i*2+1] = (drflac_int32)right;
87689 }
87690 }
87691 #endif
87692 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87693 {
87694 drflac_uint64 i;
87695 drflac_uint64 frameCount4 = frameCount >> 2;
87696 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87697 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87698 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87699 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87700 for (i = 0; i < frameCount4; ++i) {
87701 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
87702 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
87703 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
87704 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
87705 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
87706 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
87707 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
87708 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
87709 drflac_uint32 left0 = right0 + side0;
87710 drflac_uint32 left1 = right1 + side1;
87711 drflac_uint32 left2 = right2 + side2;
87712 drflac_uint32 left3 = right3 + side3;
87713 pOutputSamples[i*8+0] = (drflac_int32)left0;
87714 pOutputSamples[i*8+1] = (drflac_int32)right0;
87715 pOutputSamples[i*8+2] = (drflac_int32)left1;
87716 pOutputSamples[i*8+3] = (drflac_int32)right1;
87717 pOutputSamples[i*8+4] = (drflac_int32)left2;
87718 pOutputSamples[i*8+5] = (drflac_int32)right2;
87719 pOutputSamples[i*8+6] = (drflac_int32)left3;
87720 pOutputSamples[i*8+7] = (drflac_int32)right3;
87721 }
87722 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87723 drflac_uint32 side = pInputSamples0U32[i] << shift0;
87724 drflac_uint32 right = pInputSamples1U32[i] << shift1;
87725 drflac_uint32 left = right + side;
87726 pOutputSamples[i*2+0] = (drflac_int32)left;
87727 pOutputSamples[i*2+1] = (drflac_int32)right;
87728 }
87729 }
87730 #if defined(DRFLAC_SUPPORT_SSE2)
87731 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87732 {
87733 drflac_uint64 i;
87734 drflac_uint64 frameCount4 = frameCount >> 2;
87735 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87736 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87737 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87738 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87739 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
87740 for (i = 0; i < frameCount4; ++i) {
87741 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
87742 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
87743 __m128i left = _mm_add_epi32(right, side);
87744 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87745 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87746 }
87747 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87748 drflac_uint32 side = pInputSamples0U32[i] << shift0;
87749 drflac_uint32 right = pInputSamples1U32[i] << shift1;
87750 drflac_uint32 left = right + side;
87751 pOutputSamples[i*2+0] = (drflac_int32)left;
87752 pOutputSamples[i*2+1] = (drflac_int32)right;
87753 }
87754 }
87755 #endif
87756 #if defined(DRFLAC_SUPPORT_NEON)
87757 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87758 {
87759 drflac_uint64 i;
87760 drflac_uint64 frameCount4 = frameCount >> 2;
87761 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87762 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87763 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87764 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87765 int32x4_t shift0_4;
87766 int32x4_t shift1_4;
87767 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
87768 shift0_4 = vdupq_n_s32(shift0);
87769 shift1_4 = vdupq_n_s32(shift1);
87770 for (i = 0; i < frameCount4; ++i) {
87771 uint32x4_t side;
87772 uint32x4_t right;
87773 uint32x4_t left;
87774 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
87775 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
87776 left = vaddq_u32(right, side);
87777 drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
87778 }
87779 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87780 drflac_uint32 side = pInputSamples0U32[i] << shift0;
87781 drflac_uint32 right = pInputSamples1U32[i] << shift1;
87782 drflac_uint32 left = right + side;
87783 pOutputSamples[i*2+0] = (drflac_int32)left;
87784 pOutputSamples[i*2+1] = (drflac_int32)right;
87785 }
87786 }
87787 #endif
87788 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87789 {
87790 #if defined(DRFLAC_SUPPORT_SSE2)
87791 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
87792 drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87793 } else
87794 #elif defined(DRFLAC_SUPPORT_NEON)
87795 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
87796 drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87797 } else
87798 #endif
87799 {
87800 #if 0
87801 drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87802 #else
87803 drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
87804 #endif
87805 }
87806 }
87807 #if 0
87808 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87809 {
87810 for (drflac_uint64 i = 0; i < frameCount; ++i) {
87811 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87812 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87813 mid = (mid << 1) | (side & 0x01);
87814 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
87815 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
87816 }
87817 }
87818 #endif
87819 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87820 {
87821 drflac_uint64 i;
87822 drflac_uint64 frameCount4 = frameCount >> 2;
87823 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87824 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87825 drflac_int32 shift = unusedBitsPerSample;
87826 if (shift > 0) {
87827 shift -= 1;
87828 for (i = 0; i < frameCount4; ++i) {
87829 drflac_uint32 temp0L;
87830 drflac_uint32 temp1L;
87831 drflac_uint32 temp2L;
87832 drflac_uint32 temp3L;
87833 drflac_uint32 temp0R;
87834 drflac_uint32 temp1R;
87835 drflac_uint32 temp2R;
87836 drflac_uint32 temp3R;
87837 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87838 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87839 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87840 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87841 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87842 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87843 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87844 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87845 mid0 = (mid0 << 1) | (side0 & 0x01);
87846 mid1 = (mid1 << 1) | (side1 & 0x01);
87847 mid2 = (mid2 << 1) | (side2 & 0x01);
87848 mid3 = (mid3 << 1) | (side3 & 0x01);
87849 temp0L = (mid0 + side0) << shift;
87850 temp1L = (mid1 + side1) << shift;
87851 temp2L = (mid2 + side2) << shift;
87852 temp3L = (mid3 + side3) << shift;
87853 temp0R = (mid0 - side0) << shift;
87854 temp1R = (mid1 - side1) << shift;
87855 temp2R = (mid2 - side2) << shift;
87856 temp3R = (mid3 - side3) << shift;
87857 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
87858 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
87859 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
87860 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
87861 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
87862 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
87863 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
87864 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
87865 }
87866 } else {
87867 for (i = 0; i < frameCount4; ++i) {
87868 drflac_uint32 temp0L;
87869 drflac_uint32 temp1L;
87870 drflac_uint32 temp2L;
87871 drflac_uint32 temp3L;
87872 drflac_uint32 temp0R;
87873 drflac_uint32 temp1R;
87874 drflac_uint32 temp2R;
87875 drflac_uint32 temp3R;
87876 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87877 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87878 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87879 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87880 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87881 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87882 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87883 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87884 mid0 = (mid0 << 1) | (side0 & 0x01);
87885 mid1 = (mid1 << 1) | (side1 & 0x01);
87886 mid2 = (mid2 << 1) | (side2 & 0x01);
87887 mid3 = (mid3 << 1) | (side3 & 0x01);
87888 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
87889 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
87890 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
87891 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
87892 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
87893 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
87894 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
87895 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
87896 pOutputSamples[i*8+0] = (drflac_int32)temp0L;
87897 pOutputSamples[i*8+1] = (drflac_int32)temp0R;
87898 pOutputSamples[i*8+2] = (drflac_int32)temp1L;
87899 pOutputSamples[i*8+3] = (drflac_int32)temp1R;
87900 pOutputSamples[i*8+4] = (drflac_int32)temp2L;
87901 pOutputSamples[i*8+5] = (drflac_int32)temp2R;
87902 pOutputSamples[i*8+6] = (drflac_int32)temp3L;
87903 pOutputSamples[i*8+7] = (drflac_int32)temp3R;
87904 }
87905 }
87906 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87907 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87908 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87909 mid = (mid << 1) | (side & 0x01);
87910 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample);
87911 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample);
87912 }
87913 }
87914 #if defined(DRFLAC_SUPPORT_SSE2)
87915 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87916 {
87917 drflac_uint64 i;
87918 drflac_uint64 frameCount4 = frameCount >> 2;
87919 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87920 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87921 drflac_int32 shift = unusedBitsPerSample;
87922 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
87923 if (shift == 0) {
87924 for (i = 0; i < frameCount4; ++i) {
87925 __m128i mid;
87926 __m128i side;
87927 __m128i left;
87928 __m128i right;
87929 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87930 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87931 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
87932 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
87933 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
87934 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87935 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87936 }
87937 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87938 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87939 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87940 mid = (mid << 1) | (side & 0x01);
87941 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
87942 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
87943 }
87944 } else {
87945 shift -= 1;
87946 for (i = 0; i < frameCount4; ++i) {
87947 __m128i mid;
87948 __m128i side;
87949 __m128i left;
87950 __m128i right;
87951 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87952 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87953 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
87954 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
87955 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
87956 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
87957 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
87958 }
87959 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87960 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87961 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
87962 mid = (mid << 1) | (side & 0x01);
87963 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
87964 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
87965 }
87966 }
87967 }
87968 #endif
87969 #if defined(DRFLAC_SUPPORT_NEON)
87970 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
87971 {
87972 drflac_uint64 i;
87973 drflac_uint64 frameCount4 = frameCount >> 2;
87974 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
87975 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
87976 drflac_int32 shift = unusedBitsPerSample;
87977 int32x4_t wbpsShift0_4;
87978 int32x4_t wbpsShift1_4;
87979 uint32x4_t one4;
87980 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
87981 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
87982 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
87983 one4 = vdupq_n_u32(1);
87984 if (shift == 0) {
87985 for (i = 0; i < frameCount4; ++i) {
87986 uint32x4_t mid;
87987 uint32x4_t side;
87988 int32x4_t left;
87989 int32x4_t right;
87990 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
87991 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
87992 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
87993 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
87994 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
87995 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
87996 }
87997 for (i = (frameCount4 << 2); i < frameCount; ++i) {
87998 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
87999 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88000 mid = (mid << 1) | (side & 0x01);
88001 pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1;
88002 pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1;
88003 }
88004 } else {
88005 int32x4_t shift4;
88006 shift -= 1;
88007 shift4 = vdupq_n_s32(shift);
88008 for (i = 0; i < frameCount4; ++i) {
88009 uint32x4_t mid;
88010 uint32x4_t side;
88011 int32x4_t left;
88012 int32x4_t right;
88013 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
88014 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
88015 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
88016 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
88017 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
88018 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
88019 }
88020 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88021 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88022 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88023 mid = (mid << 1) | (side & 0x01);
88024 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift);
88025 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift);
88026 }
88027 }
88028 }
88029 #endif
88030 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
88031 {
88032 #if defined(DRFLAC_SUPPORT_SSE2)
88033 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88034 drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88035 } else
88036 #elif defined(DRFLAC_SUPPORT_NEON)
88037 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88038 drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88039 } else
88040 #endif
88041 {
88042 #if 0
88043 drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88044 #else
88045 drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88046 #endif
88047 }
88048 }
88049 #if 0
88050 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
88051 {
88052 for (drflac_uint64 i = 0; i < frameCount; ++i) {
88053 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
88054 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
88055 }
88056 }
88057 #endif
88058 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
88059 {
88060 drflac_uint64 i;
88061 drflac_uint64 frameCount4 = frameCount >> 2;
88062 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88063 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88064 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88065 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88066 for (i = 0; i < frameCount4; ++i) {
88067 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
88068 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
88069 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
88070 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
88071 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
88072 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
88073 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
88074 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
88075 pOutputSamples[i*8+0] = (drflac_int32)tempL0;
88076 pOutputSamples[i*8+1] = (drflac_int32)tempR0;
88077 pOutputSamples[i*8+2] = (drflac_int32)tempL1;
88078 pOutputSamples[i*8+3] = (drflac_int32)tempR1;
88079 pOutputSamples[i*8+4] = (drflac_int32)tempL2;
88080 pOutputSamples[i*8+5] = (drflac_int32)tempR2;
88081 pOutputSamples[i*8+6] = (drflac_int32)tempL3;
88082 pOutputSamples[i*8+7] = (drflac_int32)tempR3;
88083 }
88084 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88085 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
88086 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
88087 }
88088 }
88089 #if defined(DRFLAC_SUPPORT_SSE2)
88090 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
88091 {
88092 drflac_uint64 i;
88093 drflac_uint64 frameCount4 = frameCount >> 2;
88094 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88095 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88096 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88097 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88098 for (i = 0; i < frameCount4; ++i) {
88099 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88100 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88101 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
88102 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
88103 }
88104 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88105 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
88106 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
88107 }
88108 }
88109 #endif
88110 #if defined(DRFLAC_SUPPORT_NEON)
88111 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
88112 {
88113 drflac_uint64 i;
88114 drflac_uint64 frameCount4 = frameCount >> 2;
88115 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88116 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88117 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88118 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88119 int32x4_t shift4_0 = vdupq_n_s32(shift0);
88120 int32x4_t shift4_1 = vdupq_n_s32(shift1);
88121 for (i = 0; i < frameCount4; ++i) {
88122 int32x4_t left;
88123 int32x4_t right;
88124 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));
88125 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));
88126 drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
88127 }
88128 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88129 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0);
88130 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1);
88131 }
88132 }
88133 #endif
88134 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
88135 {
88136 #if defined(DRFLAC_SUPPORT_SSE2)
88137 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88138 drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88139 } else
88140 #elif defined(DRFLAC_SUPPORT_NEON)
88141 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88142 drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88143 } else
88144 #endif
88145 {
88146 #if 0
88147 drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88148 #else
88149 drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88150 #endif
88151 }
88152 }
88153 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
88154 {
88155 drflac_uint64 framesRead;
88156 drflac_uint32 unusedBitsPerSample;
88157 if (pFlac == NULL || framesToRead == 0) {
88158 return 0;
88159 }
88160 if (pBufferOut == NULL) {
88161 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
88162 }
88163 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
88164 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
88165 framesRead = 0;
88166 while (framesToRead > 0) {
88167 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
88168 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
88169 break;
88170 }
88171 } else {
88172 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
88173 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
88174 drflac_uint64 frameCountThisIteration = framesToRead;
88175 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
88176 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
88177 }
88178 if (channelCount == 2) {
88179 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
88180 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
88181 switch (pFlac->currentFLACFrame.header.channelAssignment)
88182 {
88183 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
88184 {
88185 drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88186 } break;
88187 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
88188 {
88189 drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88190 } break;
88191 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
88192 {
88193 drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88194 } break;
88195 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
88196 default:
88197 {
88198 drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88199 } break;
88200 }
88201 } else {
88202 drflac_uint64 i;
88203 for (i = 0; i < frameCountThisIteration; ++i) {
88204 unsigned int j;
88205 for (j = 0; j < channelCount; ++j) {
88206 pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
88207 }
88208 }
88209 }
88210 framesRead += frameCountThisIteration;
88211 pBufferOut += frameCountThisIteration * channelCount;
88212 framesToRead -= frameCountThisIteration;
88213 pFlac->currentPCMFrame += frameCountThisIteration;
88214 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
88215 }
88216 }
88217 return framesRead;
88218 }
88219 #if 0
88220 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88221 {
88222 drflac_uint64 i;
88223 for (i = 0; i < frameCount; ++i) {
88224 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88225 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88226 drflac_uint32 right = left - side;
88227 left >>= 16;
88228 right >>= 16;
88229 pOutputSamples[i*2+0] = (drflac_int16)left;
88230 pOutputSamples[i*2+1] = (drflac_int16)right;
88231 }
88232 }
88233 #endif
88234 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88235 {
88236 drflac_uint64 i;
88237 drflac_uint64 frameCount4 = frameCount >> 2;
88238 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88239 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88240 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88241 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88242 for (i = 0; i < frameCount4; ++i) {
88243 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
88244 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
88245 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
88246 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
88247 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
88248 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
88249 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
88250 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
88251 drflac_uint32 right0 = left0 - side0;
88252 drflac_uint32 right1 = left1 - side1;
88253 drflac_uint32 right2 = left2 - side2;
88254 drflac_uint32 right3 = left3 - side3;
88255 left0 >>= 16;
88256 left1 >>= 16;
88257 left2 >>= 16;
88258 left3 >>= 16;
88259 right0 >>= 16;
88260 right1 >>= 16;
88261 right2 >>= 16;
88262 right3 >>= 16;
88263 pOutputSamples[i*8+0] = (drflac_int16)left0;
88264 pOutputSamples[i*8+1] = (drflac_int16)right0;
88265 pOutputSamples[i*8+2] = (drflac_int16)left1;
88266 pOutputSamples[i*8+3] = (drflac_int16)right1;
88267 pOutputSamples[i*8+4] = (drflac_int16)left2;
88268 pOutputSamples[i*8+5] = (drflac_int16)right2;
88269 pOutputSamples[i*8+6] = (drflac_int16)left3;
88270 pOutputSamples[i*8+7] = (drflac_int16)right3;
88271 }
88272 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88273 drflac_uint32 left = pInputSamples0U32[i] << shift0;
88274 drflac_uint32 side = pInputSamples1U32[i] << shift1;
88275 drflac_uint32 right = left - side;
88276 left >>= 16;
88277 right >>= 16;
88278 pOutputSamples[i*2+0] = (drflac_int16)left;
88279 pOutputSamples[i*2+1] = (drflac_int16)right;
88280 }
88281 }
88282 #if defined(DRFLAC_SUPPORT_SSE2)
88283 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88284 {
88285 drflac_uint64 i;
88286 drflac_uint64 frameCount4 = frameCount >> 2;
88287 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88288 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88289 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88290 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88291 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
88292 for (i = 0; i < frameCount4; ++i) {
88293 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88294 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88295 __m128i right = _mm_sub_epi32(left, side);
88296 left = _mm_srai_epi32(left, 16);
88297 right = _mm_srai_epi32(right, 16);
88298 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
88299 }
88300 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88301 drflac_uint32 left = pInputSamples0U32[i] << shift0;
88302 drflac_uint32 side = pInputSamples1U32[i] << shift1;
88303 drflac_uint32 right = left - side;
88304 left >>= 16;
88305 right >>= 16;
88306 pOutputSamples[i*2+0] = (drflac_int16)left;
88307 pOutputSamples[i*2+1] = (drflac_int16)right;
88308 }
88309 }
88310 #endif
88311 #if defined(DRFLAC_SUPPORT_NEON)
88312 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88313 {
88314 drflac_uint64 i;
88315 drflac_uint64 frameCount4 = frameCount >> 2;
88316 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88317 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88318 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88319 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88320 int32x4_t shift0_4;
88321 int32x4_t shift1_4;
88322 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
88323 shift0_4 = vdupq_n_s32(shift0);
88324 shift1_4 = vdupq_n_s32(shift1);
88325 for (i = 0; i < frameCount4; ++i) {
88326 uint32x4_t left;
88327 uint32x4_t side;
88328 uint32x4_t right;
88329 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
88330 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
88331 right = vsubq_u32(left, side);
88332 left = vshrq_n_u32(left, 16);
88333 right = vshrq_n_u32(right, 16);
88334 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
88335 }
88336 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88337 drflac_uint32 left = pInputSamples0U32[i] << shift0;
88338 drflac_uint32 side = pInputSamples1U32[i] << shift1;
88339 drflac_uint32 right = left - side;
88340 left >>= 16;
88341 right >>= 16;
88342 pOutputSamples[i*2+0] = (drflac_int16)left;
88343 pOutputSamples[i*2+1] = (drflac_int16)right;
88344 }
88345 }
88346 #endif
88347 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88348 {
88349 #if defined(DRFLAC_SUPPORT_SSE2)
88350 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88351 drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88352 } else
88353 #elif defined(DRFLAC_SUPPORT_NEON)
88354 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88355 drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88356 } else
88357 #endif
88358 {
88359 #if 0
88360 drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88361 #else
88362 drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88363 #endif
88364 }
88365 }
88366 #if 0
88367 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88368 {
88369 drflac_uint64 i;
88370 for (i = 0; i < frameCount; ++i) {
88371 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88372 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88373 drflac_uint32 left = right + side;
88374 left >>= 16;
88375 right >>= 16;
88376 pOutputSamples[i*2+0] = (drflac_int16)left;
88377 pOutputSamples[i*2+1] = (drflac_int16)right;
88378 }
88379 }
88380 #endif
88381 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88382 {
88383 drflac_uint64 i;
88384 drflac_uint64 frameCount4 = frameCount >> 2;
88385 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88386 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88387 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88388 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88389 for (i = 0; i < frameCount4; ++i) {
88390 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
88391 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
88392 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
88393 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
88394 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
88395 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
88396 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
88397 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
88398 drflac_uint32 left0 = right0 + side0;
88399 drflac_uint32 left1 = right1 + side1;
88400 drflac_uint32 left2 = right2 + side2;
88401 drflac_uint32 left3 = right3 + side3;
88402 left0 >>= 16;
88403 left1 >>= 16;
88404 left2 >>= 16;
88405 left3 >>= 16;
88406 right0 >>= 16;
88407 right1 >>= 16;
88408 right2 >>= 16;
88409 right3 >>= 16;
88410 pOutputSamples[i*8+0] = (drflac_int16)left0;
88411 pOutputSamples[i*8+1] = (drflac_int16)right0;
88412 pOutputSamples[i*8+2] = (drflac_int16)left1;
88413 pOutputSamples[i*8+3] = (drflac_int16)right1;
88414 pOutputSamples[i*8+4] = (drflac_int16)left2;
88415 pOutputSamples[i*8+5] = (drflac_int16)right2;
88416 pOutputSamples[i*8+6] = (drflac_int16)left3;
88417 pOutputSamples[i*8+7] = (drflac_int16)right3;
88418 }
88419 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88420 drflac_uint32 side = pInputSamples0U32[i] << shift0;
88421 drflac_uint32 right = pInputSamples1U32[i] << shift1;
88422 drflac_uint32 left = right + side;
88423 left >>= 16;
88424 right >>= 16;
88425 pOutputSamples[i*2+0] = (drflac_int16)left;
88426 pOutputSamples[i*2+1] = (drflac_int16)right;
88427 }
88428 }
88429 #if defined(DRFLAC_SUPPORT_SSE2)
88430 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88431 {
88432 drflac_uint64 i;
88433 drflac_uint64 frameCount4 = frameCount >> 2;
88434 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88435 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88436 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88437 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88438 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
88439 for (i = 0; i < frameCount4; ++i) {
88440 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88441 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88442 __m128i left = _mm_add_epi32(right, side);
88443 left = _mm_srai_epi32(left, 16);
88444 right = _mm_srai_epi32(right, 16);
88445 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
88446 }
88447 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88448 drflac_uint32 side = pInputSamples0U32[i] << shift0;
88449 drflac_uint32 right = pInputSamples1U32[i] << shift1;
88450 drflac_uint32 left = right + side;
88451 left >>= 16;
88452 right >>= 16;
88453 pOutputSamples[i*2+0] = (drflac_int16)left;
88454 pOutputSamples[i*2+1] = (drflac_int16)right;
88455 }
88456 }
88457 #endif
88458 #if defined(DRFLAC_SUPPORT_NEON)
88459 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88460 {
88461 drflac_uint64 i;
88462 drflac_uint64 frameCount4 = frameCount >> 2;
88463 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88464 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88465 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88466 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88467 int32x4_t shift0_4;
88468 int32x4_t shift1_4;
88469 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
88470 shift0_4 = vdupq_n_s32(shift0);
88471 shift1_4 = vdupq_n_s32(shift1);
88472 for (i = 0; i < frameCount4; ++i) {
88473 uint32x4_t side;
88474 uint32x4_t right;
88475 uint32x4_t left;
88476 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
88477 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
88478 left = vaddq_u32(right, side);
88479 left = vshrq_n_u32(left, 16);
88480 right = vshrq_n_u32(right, 16);
88481 drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
88482 }
88483 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88484 drflac_uint32 side = pInputSamples0U32[i] << shift0;
88485 drflac_uint32 right = pInputSamples1U32[i] << shift1;
88486 drflac_uint32 left = right + side;
88487 left >>= 16;
88488 right >>= 16;
88489 pOutputSamples[i*2+0] = (drflac_int16)left;
88490 pOutputSamples[i*2+1] = (drflac_int16)right;
88491 }
88492 }
88493 #endif
88494 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88495 {
88496 #if defined(DRFLAC_SUPPORT_SSE2)
88497 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88498 drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88499 } else
88500 #elif defined(DRFLAC_SUPPORT_NEON)
88501 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88502 drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88503 } else
88504 #endif
88505 {
88506 #if 0
88507 drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88508 #else
88509 drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88510 #endif
88511 }
88512 }
88513 #if 0
88514 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88515 {
88516 for (drflac_uint64 i = 0; i < frameCount; ++i) {
88517 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88518 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88519 mid = (mid << 1) | (side & 0x01);
88520 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
88521 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
88522 }
88523 }
88524 #endif
88525 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88526 {
88527 drflac_uint64 i;
88528 drflac_uint64 frameCount4 = frameCount >> 2;
88529 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88530 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88531 drflac_uint32 shift = unusedBitsPerSample;
88532 if (shift > 0) {
88533 shift -= 1;
88534 for (i = 0; i < frameCount4; ++i) {
88535 drflac_uint32 temp0L;
88536 drflac_uint32 temp1L;
88537 drflac_uint32 temp2L;
88538 drflac_uint32 temp3L;
88539 drflac_uint32 temp0R;
88540 drflac_uint32 temp1R;
88541 drflac_uint32 temp2R;
88542 drflac_uint32 temp3R;
88543 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88544 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88545 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88546 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88547 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88548 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88549 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88550 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88551 mid0 = (mid0 << 1) | (side0 & 0x01);
88552 mid1 = (mid1 << 1) | (side1 & 0x01);
88553 mid2 = (mid2 << 1) | (side2 & 0x01);
88554 mid3 = (mid3 << 1) | (side3 & 0x01);
88555 temp0L = (mid0 + side0) << shift;
88556 temp1L = (mid1 + side1) << shift;
88557 temp2L = (mid2 + side2) << shift;
88558 temp3L = (mid3 + side3) << shift;
88559 temp0R = (mid0 - side0) << shift;
88560 temp1R = (mid1 - side1) << shift;
88561 temp2R = (mid2 - side2) << shift;
88562 temp3R = (mid3 - side3) << shift;
88563 temp0L >>= 16;
88564 temp1L >>= 16;
88565 temp2L >>= 16;
88566 temp3L >>= 16;
88567 temp0R >>= 16;
88568 temp1R >>= 16;
88569 temp2R >>= 16;
88570 temp3R >>= 16;
88571 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
88572 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
88573 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
88574 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
88575 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
88576 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
88577 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
88578 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
88579 }
88580 } else {
88581 for (i = 0; i < frameCount4; ++i) {
88582 drflac_uint32 temp0L;
88583 drflac_uint32 temp1L;
88584 drflac_uint32 temp2L;
88585 drflac_uint32 temp3L;
88586 drflac_uint32 temp0R;
88587 drflac_uint32 temp1R;
88588 drflac_uint32 temp2R;
88589 drflac_uint32 temp3R;
88590 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88591 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88592 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88593 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88594 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88595 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88596 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88597 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88598 mid0 = (mid0 << 1) | (side0 & 0x01);
88599 mid1 = (mid1 << 1) | (side1 & 0x01);
88600 mid2 = (mid2 << 1) | (side2 & 0x01);
88601 mid3 = (mid3 << 1) | (side3 & 0x01);
88602 temp0L = ((drflac_int32)(mid0 + side0) >> 1);
88603 temp1L = ((drflac_int32)(mid1 + side1) >> 1);
88604 temp2L = ((drflac_int32)(mid2 + side2) >> 1);
88605 temp3L = ((drflac_int32)(mid3 + side3) >> 1);
88606 temp0R = ((drflac_int32)(mid0 - side0) >> 1);
88607 temp1R = ((drflac_int32)(mid1 - side1) >> 1);
88608 temp2R = ((drflac_int32)(mid2 - side2) >> 1);
88609 temp3R = ((drflac_int32)(mid3 - side3) >> 1);
88610 temp0L >>= 16;
88611 temp1L >>= 16;
88612 temp2L >>= 16;
88613 temp3L >>= 16;
88614 temp0R >>= 16;
88615 temp1R >>= 16;
88616 temp2R >>= 16;
88617 temp3R >>= 16;
88618 pOutputSamples[i*8+0] = (drflac_int16)temp0L;
88619 pOutputSamples[i*8+1] = (drflac_int16)temp0R;
88620 pOutputSamples[i*8+2] = (drflac_int16)temp1L;
88621 pOutputSamples[i*8+3] = (drflac_int16)temp1R;
88622 pOutputSamples[i*8+4] = (drflac_int16)temp2L;
88623 pOutputSamples[i*8+5] = (drflac_int16)temp2R;
88624 pOutputSamples[i*8+6] = (drflac_int16)temp3L;
88625 pOutputSamples[i*8+7] = (drflac_int16)temp3R;
88626 }
88627 }
88628 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88629 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88630 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88631 mid = (mid << 1) | (side & 0x01);
88632 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
88633 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
88634 }
88635 }
88636 #if defined(DRFLAC_SUPPORT_SSE2)
88637 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88638 {
88639 drflac_uint64 i;
88640 drflac_uint64 frameCount4 = frameCount >> 2;
88641 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88642 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88643 drflac_uint32 shift = unusedBitsPerSample;
88644 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
88645 if (shift == 0) {
88646 for (i = 0; i < frameCount4; ++i) {
88647 __m128i mid;
88648 __m128i side;
88649 __m128i left;
88650 __m128i right;
88651 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88652 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88653 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
88654 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
88655 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
88656 left = _mm_srai_epi32(left, 16);
88657 right = _mm_srai_epi32(right, 16);
88658 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
88659 }
88660 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88661 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88662 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88663 mid = (mid << 1) | (side & 0x01);
88664 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
88665 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
88666 }
88667 } else {
88668 shift -= 1;
88669 for (i = 0; i < frameCount4; ++i) {
88670 __m128i mid;
88671 __m128i side;
88672 __m128i left;
88673 __m128i right;
88674 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88675 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88676 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
88677 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
88678 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
88679 left = _mm_srai_epi32(left, 16);
88680 right = _mm_srai_epi32(right, 16);
88681 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
88682 }
88683 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88684 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88685 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88686 mid = (mid << 1) | (side & 0x01);
88687 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
88688 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
88689 }
88690 }
88691 }
88692 #endif
88693 #if defined(DRFLAC_SUPPORT_NEON)
88694 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88695 {
88696 drflac_uint64 i;
88697 drflac_uint64 frameCount4 = frameCount >> 2;
88698 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88699 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88700 drflac_uint32 shift = unusedBitsPerSample;
88701 int32x4_t wbpsShift0_4;
88702 int32x4_t wbpsShift1_4;
88703 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
88704 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88705 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88706 if (shift == 0) {
88707 for (i = 0; i < frameCount4; ++i) {
88708 uint32x4_t mid;
88709 uint32x4_t side;
88710 int32x4_t left;
88711 int32x4_t right;
88712 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
88713 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
88714 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
88715 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
88716 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
88717 left = vshrq_n_s32(left, 16);
88718 right = vshrq_n_s32(right, 16);
88719 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
88720 }
88721 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88722 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88723 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88724 mid = (mid << 1) | (side & 0x01);
88725 pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16);
88726 pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16);
88727 }
88728 } else {
88729 int32x4_t shift4;
88730 shift -= 1;
88731 shift4 = vdupq_n_s32(shift);
88732 for (i = 0; i < frameCount4; ++i) {
88733 uint32x4_t mid;
88734 uint32x4_t side;
88735 int32x4_t left;
88736 int32x4_t right;
88737 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
88738 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
88739 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
88740 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
88741 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
88742 left = vshrq_n_s32(left, 16);
88743 right = vshrq_n_s32(right, 16);
88744 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
88745 }
88746 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88747 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88748 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88749 mid = (mid << 1) | (side & 0x01);
88750 pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
88751 pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
88752 }
88753 }
88754 }
88755 #endif
88756 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88757 {
88758 #if defined(DRFLAC_SUPPORT_SSE2)
88759 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88760 drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88761 } else
88762 #elif defined(DRFLAC_SUPPORT_NEON)
88763 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88764 drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88765 } else
88766 #endif
88767 {
88768 #if 0
88769 drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88770 #else
88771 drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88772 #endif
88773 }
88774 }
88775 #if 0
88776 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88777 {
88778 for (drflac_uint64 i = 0; i < frameCount; ++i) {
88779 pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
88780 pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
88781 }
88782 }
88783 #endif
88784 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88785 {
88786 drflac_uint64 i;
88787 drflac_uint64 frameCount4 = frameCount >> 2;
88788 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88789 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88790 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88791 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88792 for (i = 0; i < frameCount4; ++i) {
88793 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
88794 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
88795 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
88796 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
88797 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
88798 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
88799 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
88800 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
88801 tempL0 >>= 16;
88802 tempL1 >>= 16;
88803 tempL2 >>= 16;
88804 tempL3 >>= 16;
88805 tempR0 >>= 16;
88806 tempR1 >>= 16;
88807 tempR2 >>= 16;
88808 tempR3 >>= 16;
88809 pOutputSamples[i*8+0] = (drflac_int16)tempL0;
88810 pOutputSamples[i*8+1] = (drflac_int16)tempR0;
88811 pOutputSamples[i*8+2] = (drflac_int16)tempL1;
88812 pOutputSamples[i*8+3] = (drflac_int16)tempR1;
88813 pOutputSamples[i*8+4] = (drflac_int16)tempL2;
88814 pOutputSamples[i*8+5] = (drflac_int16)tempR2;
88815 pOutputSamples[i*8+6] = (drflac_int16)tempL3;
88816 pOutputSamples[i*8+7] = (drflac_int16)tempR3;
88817 }
88818 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88819 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
88820 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
88821 }
88822 }
88823 #if defined(DRFLAC_SUPPORT_SSE2)
88824 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88825 {
88826 drflac_uint64 i;
88827 drflac_uint64 frameCount4 = frameCount >> 2;
88828 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88829 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88830 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88831 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88832 for (i = 0; i < frameCount4; ++i) {
88833 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
88834 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
88835 left = _mm_srai_epi32(left, 16);
88836 right = _mm_srai_epi32(right, 16);
88837 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
88838 }
88839 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88840 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
88841 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
88842 }
88843 }
88844 #endif
88845 #if defined(DRFLAC_SUPPORT_NEON)
88846 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88847 {
88848 drflac_uint64 i;
88849 drflac_uint64 frameCount4 = frameCount >> 2;
88850 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88851 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88852 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88853 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88854 int32x4_t shift0_4 = vdupq_n_s32(shift0);
88855 int32x4_t shift1_4 = vdupq_n_s32(shift1);
88856 for (i = 0; i < frameCount4; ++i) {
88857 int32x4_t left;
88858 int32x4_t right;
88859 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
88860 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
88861 left = vshrq_n_s32(left, 16);
88862 right = vshrq_n_s32(right, 16);
88863 drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
88864 }
88865 for (i = (frameCount4 << 2); i < frameCount; ++i) {
88866 pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16);
88867 pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16);
88868 }
88869 }
88870 #endif
88871 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
88872 {
88873 #if defined(DRFLAC_SUPPORT_SSE2)
88874 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
88875 drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88876 } else
88877 #elif defined(DRFLAC_SUPPORT_NEON)
88878 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
88879 drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88880 } else
88881 #endif
88882 {
88883 #if 0
88884 drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88885 #else
88886 drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
88887 #endif
88888 }
88889 }
88890 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
88891 {
88892 drflac_uint64 framesRead;
88893 drflac_uint32 unusedBitsPerSample;
88894 if (pFlac == NULL || framesToRead == 0) {
88895 return 0;
88896 }
88897 if (pBufferOut == NULL) {
88898 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
88899 }
88900 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
88901 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
88902 framesRead = 0;
88903 while (framesToRead > 0) {
88904 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
88905 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
88906 break;
88907 }
88908 } else {
88909 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
88910 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
88911 drflac_uint64 frameCountThisIteration = framesToRead;
88912 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
88913 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
88914 }
88915 if (channelCount == 2) {
88916 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
88917 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
88918 switch (pFlac->currentFLACFrame.header.channelAssignment)
88919 {
88920 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
88921 {
88922 drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88923 } break;
88924 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
88925 {
88926 drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88927 } break;
88928 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
88929 {
88930 drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88931 } break;
88932 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
88933 default:
88934 {
88935 drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
88936 } break;
88937 }
88938 } else {
88939 drflac_uint64 i;
88940 for (i = 0; i < frameCountThisIteration; ++i) {
88941 unsigned int j;
88942 for (j = 0; j < channelCount; ++j) {
88943 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
88944 pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16);
88945 }
88946 }
88947 }
88948 framesRead += frameCountThisIteration;
88949 pBufferOut += frameCountThisIteration * channelCount;
88950 framesToRead -= frameCountThisIteration;
88951 pFlac->currentPCMFrame += frameCountThisIteration;
88952 pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
88953 }
88954 }
88955 return framesRead;
88956 }
88957 #if 0
88958 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
88959 {
88960 drflac_uint64 i;
88961 for (i = 0; i < frameCount; ++i) {
88962 drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
88963 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
88964 drflac_uint32 right = left - side;
88965 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
88966 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
88967 }
88968 }
88969 #endif
88970 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
88971 {
88972 drflac_uint64 i;
88973 drflac_uint64 frameCount4 = frameCount >> 2;
88974 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
88975 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
88976 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
88977 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
88978 float factor = 1 / 2147483648.0;
88979 for (i = 0; i < frameCount4; ++i) {
88980 drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
88981 drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
88982 drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
88983 drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
88984 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
88985 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
88986 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
88987 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
88988 drflac_uint32 right0 = left0 - side0;
88989 drflac_uint32 right1 = left1 - side1;
88990 drflac_uint32 right2 = left2 - side2;
88991 drflac_uint32 right3 = left3 - side3;
88992 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
88993 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
88994 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
88995 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
88996 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
88997 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
88998 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
88999 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
89000 }
89001 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89002 drflac_uint32 left = pInputSamples0U32[i] << shift0;
89003 drflac_uint32 side = pInputSamples1U32[i] << shift1;
89004 drflac_uint32 right = left - side;
89005 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
89006 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
89007 }
89008 }
89009 #if defined(DRFLAC_SUPPORT_SSE2)
89010 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89011 {
89012 drflac_uint64 i;
89013 drflac_uint64 frameCount4 = frameCount >> 2;
89014 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89015 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89016 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89017 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89018 __m128 factor;
89019 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
89020 factor = _mm_set1_ps(1.0f / 8388608.0f);
89021 for (i = 0; i < frameCount4; ++i) {
89022 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
89023 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
89024 __m128i right = _mm_sub_epi32(left, side);
89025 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
89026 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
89027 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89028 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89029 }
89030 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89031 drflac_uint32 left = pInputSamples0U32[i] << shift0;
89032 drflac_uint32 side = pInputSamples1U32[i] << shift1;
89033 drflac_uint32 right = left - side;
89034 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
89035 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
89036 }
89037 }
89038 #endif
89039 #if defined(DRFLAC_SUPPORT_NEON)
89040 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89041 {
89042 drflac_uint64 i;
89043 drflac_uint64 frameCount4 = frameCount >> 2;
89044 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89045 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89046 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89047 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89048 float32x4_t factor4;
89049 int32x4_t shift0_4;
89050 int32x4_t shift1_4;
89051 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
89052 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
89053 shift0_4 = vdupq_n_s32(shift0);
89054 shift1_4 = vdupq_n_s32(shift1);
89055 for (i = 0; i < frameCount4; ++i) {
89056 uint32x4_t left;
89057 uint32x4_t side;
89058 uint32x4_t right;
89059 float32x4_t leftf;
89060 float32x4_t rightf;
89061 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
89062 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
89063 right = vsubq_u32(left, side);
89064 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
89065 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
89066 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89067 }
89068 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89069 drflac_uint32 left = pInputSamples0U32[i] << shift0;
89070 drflac_uint32 side = pInputSamples1U32[i] << shift1;
89071 drflac_uint32 right = left - side;
89072 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
89073 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
89074 }
89075 }
89076 #endif
89077 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89078 {
89079 #if defined(DRFLAC_SUPPORT_SSE2)
89080 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
89081 drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89082 } else
89083 #elif defined(DRFLAC_SUPPORT_NEON)
89084 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
89085 drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89086 } else
89087 #endif
89088 {
89089 #if 0
89090 drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89091 #else
89092 drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89093 #endif
89094 }
89095 }
89096 #if 0
89097 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89098 {
89099 drflac_uint64 i;
89100 for (i = 0; i < frameCount; ++i) {
89101 drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89102 drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89103 drflac_uint32 left = right + side;
89104 pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0);
89105 pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0);
89106 }
89107 }
89108 #endif
89109 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89110 {
89111 drflac_uint64 i;
89112 drflac_uint64 frameCount4 = frameCount >> 2;
89113 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89114 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89115 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89116 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89117 float factor = 1 / 2147483648.0;
89118 for (i = 0; i < frameCount4; ++i) {
89119 drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
89120 drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
89121 drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
89122 drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
89123 drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
89124 drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
89125 drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
89126 drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
89127 drflac_uint32 left0 = right0 + side0;
89128 drflac_uint32 left1 = right1 + side1;
89129 drflac_uint32 left2 = right2 + side2;
89130 drflac_uint32 left3 = right3 + side3;
89131 pOutputSamples[i*8+0] = (drflac_int32)left0 * factor;
89132 pOutputSamples[i*8+1] = (drflac_int32)right0 * factor;
89133 pOutputSamples[i*8+2] = (drflac_int32)left1 * factor;
89134 pOutputSamples[i*8+3] = (drflac_int32)right1 * factor;
89135 pOutputSamples[i*8+4] = (drflac_int32)left2 * factor;
89136 pOutputSamples[i*8+5] = (drflac_int32)right2 * factor;
89137 pOutputSamples[i*8+6] = (drflac_int32)left3 * factor;
89138 pOutputSamples[i*8+7] = (drflac_int32)right3 * factor;
89139 }
89140 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89141 drflac_uint32 side = pInputSamples0U32[i] << shift0;
89142 drflac_uint32 right = pInputSamples1U32[i] << shift1;
89143 drflac_uint32 left = right + side;
89144 pOutputSamples[i*2+0] = (drflac_int32)left * factor;
89145 pOutputSamples[i*2+1] = (drflac_int32)right * factor;
89146 }
89147 }
89148 #if defined(DRFLAC_SUPPORT_SSE2)
89149 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89150 {
89151 drflac_uint64 i;
89152 drflac_uint64 frameCount4 = frameCount >> 2;
89153 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89154 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89155 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89156 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89157 __m128 factor;
89158 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
89159 factor = _mm_set1_ps(1.0f / 8388608.0f);
89160 for (i = 0; i < frameCount4; ++i) {
89161 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
89162 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
89163 __m128i left = _mm_add_epi32(right, side);
89164 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
89165 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
89166 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89167 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89168 }
89169 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89170 drflac_uint32 side = pInputSamples0U32[i] << shift0;
89171 drflac_uint32 right = pInputSamples1U32[i] << shift1;
89172 drflac_uint32 left = right + side;
89173 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
89174 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
89175 }
89176 }
89177 #endif
89178 #if defined(DRFLAC_SUPPORT_NEON)
89179 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89180 {
89181 drflac_uint64 i;
89182 drflac_uint64 frameCount4 = frameCount >> 2;
89183 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89184 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89185 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89186 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89187 float32x4_t factor4;
89188 int32x4_t shift0_4;
89189 int32x4_t shift1_4;
89190 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
89191 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
89192 shift0_4 = vdupq_n_s32(shift0);
89193 shift1_4 = vdupq_n_s32(shift1);
89194 for (i = 0; i < frameCount4; ++i) {
89195 uint32x4_t side;
89196 uint32x4_t right;
89197 uint32x4_t left;
89198 float32x4_t leftf;
89199 float32x4_t rightf;
89200 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
89201 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
89202 left = vaddq_u32(right, side);
89203 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
89204 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
89205 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89206 }
89207 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89208 drflac_uint32 side = pInputSamples0U32[i] << shift0;
89209 drflac_uint32 right = pInputSamples1U32[i] << shift1;
89210 drflac_uint32 left = right + side;
89211 pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f;
89212 pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f;
89213 }
89214 }
89215 #endif
89216 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89217 {
89218 #if defined(DRFLAC_SUPPORT_SSE2)
89219 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
89220 drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89221 } else
89222 #elif defined(DRFLAC_SUPPORT_NEON)
89223 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
89224 drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89225 } else
89226 #endif
89227 {
89228 #if 0
89229 drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89230 #else
89231 drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89232 #endif
89233 }
89234 }
89235 #if 0
89236 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89237 {
89238 for (drflac_uint64 i = 0; i < frameCount; ++i) {
89239 drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89240 drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89241 mid = (mid << 1) | (side & 0x01);
89242 pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
89243 pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
89244 }
89245 }
89246 #endif
89247 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89248 {
89249 drflac_uint64 i;
89250 drflac_uint64 frameCount4 = frameCount >> 2;
89251 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89252 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89253 drflac_uint32 shift = unusedBitsPerSample;
89254 float factor = 1 / 2147483648.0;
89255 if (shift > 0) {
89256 shift -= 1;
89257 for (i = 0; i < frameCount4; ++i) {
89258 drflac_uint32 temp0L;
89259 drflac_uint32 temp1L;
89260 drflac_uint32 temp2L;
89261 drflac_uint32 temp3L;
89262 drflac_uint32 temp0R;
89263 drflac_uint32 temp1R;
89264 drflac_uint32 temp2R;
89265 drflac_uint32 temp3R;
89266 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89267 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89268 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89269 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89270 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89271 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89272 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89273 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89274 mid0 = (mid0 << 1) | (side0 & 0x01);
89275 mid1 = (mid1 << 1) | (side1 & 0x01);
89276 mid2 = (mid2 << 1) | (side2 & 0x01);
89277 mid3 = (mid3 << 1) | (side3 & 0x01);
89278 temp0L = (mid0 + side0) << shift;
89279 temp1L = (mid1 + side1) << shift;
89280 temp2L = (mid2 + side2) << shift;
89281 temp3L = (mid3 + side3) << shift;
89282 temp0R = (mid0 - side0) << shift;
89283 temp1R = (mid1 - side1) << shift;
89284 temp2R = (mid2 - side2) << shift;
89285 temp3R = (mid3 - side3) << shift;
89286 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
89287 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
89288 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
89289 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
89290 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
89291 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
89292 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
89293 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
89294 }
89295 } else {
89296 for (i = 0; i < frameCount4; ++i) {
89297 drflac_uint32 temp0L;
89298 drflac_uint32 temp1L;
89299 drflac_uint32 temp2L;
89300 drflac_uint32 temp3L;
89301 drflac_uint32 temp0R;
89302 drflac_uint32 temp1R;
89303 drflac_uint32 temp2R;
89304 drflac_uint32 temp3R;
89305 drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89306 drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89307 drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89308 drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89309 drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89310 drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89311 drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89312 drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89313 mid0 = (mid0 << 1) | (side0 & 0x01);
89314 mid1 = (mid1 << 1) | (side1 & 0x01);
89315 mid2 = (mid2 << 1) | (side2 & 0x01);
89316 mid3 = (mid3 << 1) | (side3 & 0x01);
89317 temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1);
89318 temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1);
89319 temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1);
89320 temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1);
89321 temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1);
89322 temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1);
89323 temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1);
89324 temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1);
89325 pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor;
89326 pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor;
89327 pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor;
89328 pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor;
89329 pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor;
89330 pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor;
89331 pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor;
89332 pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor;
89333 }
89334 }
89335 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89336 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89337 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89338 mid = (mid << 1) | (side & 0x01);
89339 pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;
89340 pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;
89341 }
89342 }
89343 #if defined(DRFLAC_SUPPORT_SSE2)
89344 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89345 {
89346 drflac_uint64 i;
89347 drflac_uint64 frameCount4 = frameCount >> 2;
89348 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89349 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89350 drflac_uint32 shift = unusedBitsPerSample - 8;
89351 float factor;
89352 __m128 factor128;
89353 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
89354 factor = 1.0f / 8388608.0f;
89355 factor128 = _mm_set1_ps(factor);
89356 if (shift == 0) {
89357 for (i = 0; i < frameCount4; ++i) {
89358 __m128i mid;
89359 __m128i side;
89360 __m128i tempL;
89361 __m128i tempR;
89362 __m128 leftf;
89363 __m128 rightf;
89364 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89365 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89366 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
89367 tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
89368 tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
89369 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
89370 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
89371 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89372 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89373 }
89374 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89375 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89376 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89377 mid = (mid << 1) | (side & 0x01);
89378 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
89379 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
89380 }
89381 } else {
89382 shift -= 1;
89383 for (i = 0; i < frameCount4; ++i) {
89384 __m128i mid;
89385 __m128i side;
89386 __m128i tempL;
89387 __m128i tempR;
89388 __m128 leftf;
89389 __m128 rightf;
89390 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89391 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89392 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
89393 tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
89394 tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
89395 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
89396 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
89397 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89398 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89399 }
89400 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89401 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89402 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89403 mid = (mid << 1) | (side & 0x01);
89404 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
89405 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
89406 }
89407 }
89408 }
89409 #endif
89410 #if defined(DRFLAC_SUPPORT_NEON)
89411 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89412 {
89413 drflac_uint64 i;
89414 drflac_uint64 frameCount4 = frameCount >> 2;
89415 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89416 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89417 drflac_uint32 shift = unusedBitsPerSample - 8;
89418 float factor;
89419 float32x4_t factor4;
89420 int32x4_t shift4;
89421 int32x4_t wbps0_4;
89422 int32x4_t wbps1_4;
89423 DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
89424 factor = 1.0f / 8388608.0f;
89425 factor4 = vdupq_n_f32(factor);
89426 wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89427 wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89428 if (shift == 0) {
89429 for (i = 0; i < frameCount4; ++i) {
89430 int32x4_t lefti;
89431 int32x4_t righti;
89432 float32x4_t leftf;
89433 float32x4_t rightf;
89434 uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
89435 uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
89436 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
89437 lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
89438 righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
89439 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
89440 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
89441 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89442 }
89443 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89444 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89445 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89446 mid = (mid << 1) | (side & 0x01);
89447 pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor;
89448 pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor;
89449 }
89450 } else {
89451 shift -= 1;
89452 shift4 = vdupq_n_s32(shift);
89453 for (i = 0; i < frameCount4; ++i) {
89454 uint32x4_t mid;
89455 uint32x4_t side;
89456 int32x4_t lefti;
89457 int32x4_t righti;
89458 float32x4_t leftf;
89459 float32x4_t rightf;
89460 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
89461 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
89462 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
89463 lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
89464 righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
89465 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
89466 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
89467 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89468 }
89469 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89470 drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89471 drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89472 mid = (mid << 1) | (side & 0x01);
89473 pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor;
89474 pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor;
89475 }
89476 }
89477 }
89478 #endif
89479 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89480 {
89481 #if defined(DRFLAC_SUPPORT_SSE2)
89482 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
89483 drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89484 } else
89485 #elif defined(DRFLAC_SUPPORT_NEON)
89486 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
89487 drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89488 } else
89489 #endif
89490 {
89491 #if 0
89492 drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89493 #else
89494 drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89495 #endif
89496 }
89497 }
89498 #if 0
89499 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89500 {
89501 for (drflac_uint64 i = 0; i < frameCount; ++i) {
89502 pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
89503 pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
89504 }
89505 }
89506 #endif
89507 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89508 {
89509 drflac_uint64 i;
89510 drflac_uint64 frameCount4 = frameCount >> 2;
89511 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89512 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89513 drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89514 drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89515 float factor = 1 / 2147483648.0;
89516 for (i = 0; i < frameCount4; ++i) {
89517 drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
89518 drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
89519 drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
89520 drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
89521 drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
89522 drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
89523 drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
89524 drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
89525 pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor;
89526 pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor;
89527 pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor;
89528 pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor;
89529 pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor;
89530 pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor;
89531 pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor;
89532 pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor;
89533 }
89534 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89535 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
89536 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
89537 }
89538 }
89539 #if defined(DRFLAC_SUPPORT_SSE2)
89540 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89541 {
89542 drflac_uint64 i;
89543 drflac_uint64 frameCount4 = frameCount >> 2;
89544 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89545 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89546 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89547 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89548 float factor = 1.0f / 8388608.0f;
89549 __m128 factor128 = _mm_set1_ps(factor);
89550 for (i = 0; i < frameCount4; ++i) {
89551 __m128i lefti;
89552 __m128i righti;
89553 __m128 leftf;
89554 __m128 rightf;
89555 lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
89556 righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
89557 leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
89558 rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
89559 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
89560 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
89561 }
89562 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89563 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
89564 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
89565 }
89566 }
89567 #endif
89568 #if defined(DRFLAC_SUPPORT_NEON)
89569 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89570 {
89571 drflac_uint64 i;
89572 drflac_uint64 frameCount4 = frameCount >> 2;
89573 const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0;
89574 const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1;
89575 drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
89576 drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
89577 float factor = 1.0f / 8388608.0f;
89578 float32x4_t factor4 = vdupq_n_f32(factor);
89579 int32x4_t shift0_4 = vdupq_n_s32(shift0);
89580 int32x4_t shift1_4 = vdupq_n_s32(shift1);
89581 for (i = 0; i < frameCount4; ++i) {
89582 int32x4_t lefti;
89583 int32x4_t righti;
89584 float32x4_t leftf;
89585 float32x4_t rightf;
89586 lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
89587 righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
89588 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
89589 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
89590 drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
89591 }
89592 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89593 pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor;
89594 pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor;
89595 }
89596 }
89597 #endif
89598 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
89599 {
89600 #if defined(DRFLAC_SUPPORT_SSE2)
89601 if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
89602 drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89603 } else
89604 #elif defined(DRFLAC_SUPPORT_NEON)
89605 if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
89606 drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89607 } else
89608 #endif
89609 {
89610 #if 0
89611 drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89612 #else
89613 drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89614 #endif
89615 }
89616 }
89617 DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut)
89618 {
89619 drflac_uint64 framesRead;
89620 drflac_uint32 unusedBitsPerSample;
89621 if (pFlac == NULL || framesToRead == 0) {
89622 return 0;
89623 }
89624 if (pBufferOut == NULL) {
89625 return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
89626 }
89627 DRFLAC_ASSERT(pFlac->bitsPerSample <= 32);
89628 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
89629 framesRead = 0;
89630 while (framesToRead > 0) {
89631 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
89632 if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
89633 break;
89634 }
89635 } else {
89636 unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
89637 drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
89638 drflac_uint64 frameCountThisIteration = framesToRead;
89639 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
89640 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
89641 }
89642 if (channelCount == 2) {
89643 const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
89644 const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
89645 switch (pFlac->currentFLACFrame.header.channelAssignment)
89646 {
89647 case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
89648 {
89649 drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89650 } break;
89651 case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
89652 {
89653 drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89654 } break;
89655 case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
89656 {
89657 drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89658 } break;
89659 case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
89660 default:
89661 {
89662 drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
89663 } break;
89664 }
89665 } else {
89666 drflac_uint64 i;
89667 for (i = 0; i < frameCountThisIteration; ++i) {
89668 unsigned int j;
89669 for (j = 0; j < channelCount; ++j) {
89670 drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
89671 pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);
89672 }
89673 }
89674 }
89675 framesRead += frameCountThisIteration;
89676 pBufferOut += frameCountThisIteration * channelCount;
89677 framesToRead -= frameCountThisIteration;
89678 pFlac->currentPCMFrame += frameCountThisIteration;
89679 pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
89680 }
89681 }
89682 return framesRead;
89683 }
89684 DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
89685 {
89686 if (pFlac == NULL) {
89687 return DRFLAC_FALSE;
89688 }
89689 if (pFlac->currentPCMFrame == pcmFrameIndex) {
89690 return DRFLAC_TRUE;
89691 }
89692 if (pFlac->firstFLACFramePosInBytes == 0) {
89693 return DRFLAC_FALSE;
89694 }
89695 if (pcmFrameIndex == 0) {
89696 pFlac->currentPCMFrame = 0;
89697 return drflac__seek_to_first_frame(pFlac);
89698 } else {
89699 drflac_bool32 wasSuccessful = DRFLAC_FALSE;
89700 drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame;
89701 if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
89702 pcmFrameIndex = pFlac->totalPCMFrameCount;
89703 }
89704 if (pcmFrameIndex > pFlac->currentPCMFrame) {
89705 drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
89706 if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
89707 pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
89708 pFlac->currentPCMFrame = pcmFrameIndex;
89709 return DRFLAC_TRUE;
89710 }
89711 } else {
89712 drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
89713 drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
89714 drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
89715 if (currentFLACFramePCMFramesConsumed > offsetAbs) {
89716 pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
89717 pFlac->currentPCMFrame = pcmFrameIndex;
89718 return DRFLAC_TRUE;
89719 }
89720 }
89721 #ifndef DR_FLAC_NO_OGG
89722 if (pFlac->container == drflac_container_ogg)
89723 {
89724 wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);
89725 }
89726 else
89727 #endif
89728 {
89729 if (!pFlac->_noSeekTableSeek) {
89730 wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
89731 }
89732 #if !defined(DR_FLAC_NO_CRC)
89733 if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
89734 wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
89735 }
89736 #endif
89737 if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
89738 wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
89739 }
89740 }
89741 if (wasSuccessful) {
89742 pFlac->currentPCMFrame = pcmFrameIndex;
89743 } else {
89744 if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) {
89745 drflac_seek_to_pcm_frame(pFlac, 0);
89746 }
89747 }
89748 return wasSuccessful;
89749 }
89750 }
89751 #if defined(SIZE_MAX)
89752 #define DRFLAC_SIZE_MAX SIZE_MAX
89753 #else
89754 #if defined(DRFLAC_64BIT)
89755 #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
89756 #else
89757 #define DRFLAC_SIZE_MAX 0xFFFFFFFF
89758 #endif
89759 #endif
89760 #define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
89761 static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\
89762 { \
89763 type* pSampleData = NULL; \
89764 drflac_uint64 totalPCMFrameCount; \
89765 \
89766 DRFLAC_ASSERT(pFlac != NULL); \
89767 \
89768 totalPCMFrameCount = pFlac->totalPCMFrameCount; \
89769 \
89770 if (totalPCMFrameCount == 0) { \
89771 type buffer[4096]; \
89772 drflac_uint64 pcmFramesRead; \
89773 size_t sampleDataBufferSize = sizeof(buffer); \
89774 \
89775 pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
89776 if (pSampleData == NULL) { \
89777 goto on_error; \
89778 } \
89779 \
89780 while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
89781 if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
89782 type* pNewSampleData; \
89783 size_t newSampleDataBufferSize; \
89784 \
89785 newSampleDataBufferSize = sampleDataBufferSize * 2; \
89786 pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
89787 if (pNewSampleData == NULL) { \
89788 drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
89789 goto on_error; \
89790 } \
89791 \
89792 sampleDataBufferSize = newSampleDataBufferSize; \
89793 pSampleData = pNewSampleData; \
89794 } \
89795 \
89796 DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
89797 totalPCMFrameCount += pcmFramesRead; \
89798 } \
89799 \
89800 \
89801 DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
89802 } else { \
89803 drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
89804 if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \
89805 goto on_error; \
89806 } \
89807 \
89808 pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \
89809 if (pSampleData == NULL) { \
89810 goto on_error; \
89811 } \
89812 \
89813 totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
89814 } \
89815 \
89816 if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
89817 if (channelsOut) *channelsOut = pFlac->channels; \
89818 if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
89819 \
89820 drflac_close(pFlac); \
89821 return pSampleData; \
89822 \
89823 on_error: \
89824 drflac_close(pFlac); \
89825 return NULL; \
89826 }
89827 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
89828 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
89829 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
89830 DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
89831 {
89832 drflac* pFlac;
89833 if (channelsOut) {
89834 *channelsOut = 0;
89835 }
89836 if (sampleRateOut) {
89837 *sampleRateOut = 0;
89838 }
89839 if (totalPCMFrameCountOut) {
89840 *totalPCMFrameCountOut = 0;
89841 }
89842 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
89843 if (pFlac == NULL) {
89844 return NULL;
89845 }
89846 return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
89847 }
89848 DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
89849 {
89850 drflac* pFlac;
89851 if (channelsOut) {
89852 *channelsOut = 0;
89853 }
89854 if (sampleRateOut) {
89855 *sampleRateOut = 0;
89856 }
89857 if (totalPCMFrameCountOut) {
89858 *totalPCMFrameCountOut = 0;
89859 }
89860 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
89861 if (pFlac == NULL) {
89862 return NULL;
89863 }
89864 return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
89865 }
89866 DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
89867 {
89868 drflac* pFlac;
89869 if (channelsOut) {
89870 *channelsOut = 0;
89871 }
89872 if (sampleRateOut) {
89873 *sampleRateOut = 0;
89874 }
89875 if (totalPCMFrameCountOut) {
89876 *totalPCMFrameCountOut = 0;
89877 }
89878 pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
89879 if (pFlac == NULL) {
89880 return NULL;
89881 }
89882 return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
89883 }
89884 #ifndef DR_FLAC_NO_STDIO
89885 DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
89886 {
89887 drflac* pFlac;
89888 if (sampleRate) {
89889 *sampleRate = 0;
89890 }
89891 if (channels) {
89892 *channels = 0;
89893 }
89894 if (totalPCMFrameCount) {
89895 *totalPCMFrameCount = 0;
89896 }
89897 pFlac = drflac_open_file(filename, pAllocationCallbacks);
89898 if (pFlac == NULL) {
89899 return NULL;
89900 }
89901 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
89902 }
89903 DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
89904 {
89905 drflac* pFlac;
89906 if (sampleRate) {
89907 *sampleRate = 0;
89908 }
89909 if (channels) {
89910 *channels = 0;
89911 }
89912 if (totalPCMFrameCount) {
89913 *totalPCMFrameCount = 0;
89914 }
89915 pFlac = drflac_open_file(filename, pAllocationCallbacks);
89916 if (pFlac == NULL) {
89917 return NULL;
89918 }
89919 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
89920 }
89921 DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
89922 {
89923 drflac* pFlac;
89924 if (sampleRate) {
89925 *sampleRate = 0;
89926 }
89927 if (channels) {
89928 *channels = 0;
89929 }
89930 if (totalPCMFrameCount) {
89931 *totalPCMFrameCount = 0;
89932 }
89933 pFlac = drflac_open_file(filename, pAllocationCallbacks);
89934 if (pFlac == NULL) {
89935 return NULL;
89936 }
89937 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
89938 }
89939 #endif
89940 DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
89941 {
89942 drflac* pFlac;
89943 if (sampleRate) {
89944 *sampleRate = 0;
89945 }
89946 if (channels) {
89947 *channels = 0;
89948 }
89949 if (totalPCMFrameCount) {
89950 *totalPCMFrameCount = 0;
89951 }
89952 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
89953 if (pFlac == NULL) {
89954 return NULL;
89955 }
89956 return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
89957 }
89958 DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
89959 {
89960 drflac* pFlac;
89961 if (sampleRate) {
89962 *sampleRate = 0;
89963 }
89964 if (channels) {
89965 *channels = 0;
89966 }
89967 if (totalPCMFrameCount) {
89968 *totalPCMFrameCount = 0;
89969 }
89970 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
89971 if (pFlac == NULL) {
89972 return NULL;
89973 }
89974 return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
89975 }
89976 DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
89977 {
89978 drflac* pFlac;
89979 if (sampleRate) {
89980 *sampleRate = 0;
89981 }
89982 if (channels) {
89983 *channels = 0;
89984 }
89985 if (totalPCMFrameCount) {
89986 *totalPCMFrameCount = 0;
89987 }
89988 pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
89989 if (pFlac == NULL) {
89990 return NULL;
89991 }
89992 return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
89993 }
89994 DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
89995 {
89996 if (pAllocationCallbacks != NULL) {
89997 drflac__free_from_callbacks(p, pAllocationCallbacks);
89998 } else {
89999 drflac__free_default(p, NULL);
90000 }
90001 }
90002 DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments)
90003 {
90004 if (pIter == NULL) {
90005 return;
90006 }
90007 pIter->countRemaining = commentCount;
90008 pIter->pRunningData = (const char*)pComments;
90009 }
90010 DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut)
90011 {
90012 drflac_int32 length;
90013 const char* pComment;
90014 if (pCommentLengthOut) {
90015 *pCommentLengthOut = 0;
90016 }
90017 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
90018 return NULL;
90019 }
90020 length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData);
90021 pIter->pRunningData += 4;
90022 pComment = pIter->pRunningData;
90023 pIter->pRunningData += length;
90024 pIter->countRemaining -= 1;
90025 if (pCommentLengthOut) {
90026 *pCommentLengthOut = length;
90027 }
90028 return pComment;
90029 }
90030 DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData)
90031 {
90032 if (pIter == NULL) {
90033 return;
90034 }
90035 pIter->countRemaining = trackCount;
90036 pIter->pRunningData = (const char*)pTrackData;
90037 }
90038 DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack)
90039 {
90040 drflac_cuesheet_track cuesheetTrack;
90041 const char* pRunningData;
90042 drflac_uint64 offsetHi;
90043 drflac_uint64 offsetLo;
90044 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
90045 return DRFLAC_FALSE;
90046 }
90047 pRunningData = pIter->pRunningData;
90048 offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
90049 offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
90050 cuesheetTrack.offset = offsetLo | (offsetHi << 32);
90051 cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
90052 DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
90053 cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
90054 cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
90055 cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
90056 cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
90057 pIter->pRunningData = pRunningData;
90058 pIter->countRemaining -= 1;
90059 if (pCuesheetTrack) {
90060 *pCuesheetTrack = cuesheetTrack;
90061 }
90062 return DRFLAC_TRUE;
90063 }
90064 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
90065 #pragma GCC diagnostic pop
90066 #endif
90067 #endif
90068 /* dr_flac_c end */
90069 #endif /* DRFLAC_IMPLEMENTATION */
90070 #endif /* MA_NO_FLAC */
90071
90072 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
90073 #if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
90074 /* dr_mp3_c begin */
90075 #ifndef dr_mp3_c
90076 #define dr_mp3_c
90077 #include <stdlib.h>
90078 #include <string.h>
90079 #include <limits.h>
90080 DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision)
90081 {
90082 if (pMajor) {
90083 *pMajor = DRMP3_VERSION_MAJOR;
90084 }
90085 if (pMinor) {
90086 *pMinor = DRMP3_VERSION_MINOR;
90087 }
90088 if (pRevision) {
90089 *pRevision = DRMP3_VERSION_REVISION;
90090 }
90091 }
90092 DRMP3_API const char* drmp3_version_string(void)
90093 {
90094 return DRMP3_VERSION_STRING;
90095 }
90096 #if defined(__TINYC__)
90097 #define DR_MP3_NO_SIMD
90098 #endif
90099 #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
90100 #define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304
90101 #ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
90102 #define DRMP3_MAX_FRAME_SYNC_MATCHES 10
90103 #endif
90104 #define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE
90105 #define DRMP3_MAX_BITRESERVOIR_BYTES 511
90106 #define DRMP3_SHORT_BLOCK_TYPE 2
90107 #define DRMP3_STOP_BLOCK_TYPE 3
90108 #define DRMP3_MODE_MONO 3
90109 #define DRMP3_MODE_JOINT_STEREO 1
90110 #define DRMP3_HDR_SIZE 4
90111 #define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
90112 #define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
90113 #define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
90114 #define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1))
90115 #define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
90116 #define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
90117 #define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
90118 #define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
90119 #define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
90120 #define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
90121 #define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
90122 #define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
90123 #define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
90124 #define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
90125 #define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)
90126 #define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
90127 #define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
90128 #define DRMP3_BITS_DEQUANTIZER_OUT -1
90129 #define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210)
90130 #define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3)
90131 #define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a))
90132 #define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a))
90133 #if !defined(DR_MP3_NO_SIMD)
90134 #if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
90135 #define DR_MP3_ONLY_SIMD
90136 #endif
90137 #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__)))
90138 #if defined(_MSC_VER)
90139 #include <intrin.h>
90140 #endif
90141 #include <emmintrin.h>
90142 #define DRMP3_HAVE_SSE 1
90143 #define DRMP3_HAVE_SIMD 1
90144 #define DRMP3_VSTORE _mm_storeu_ps
90145 #define DRMP3_VLD _mm_loadu_ps
90146 #define DRMP3_VSET _mm_set1_ps
90147 #define DRMP3_VADD _mm_add_ps
90148 #define DRMP3_VSUB _mm_sub_ps
90149 #define DRMP3_VMUL _mm_mul_ps
90150 #define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
90151 #define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
90152 #define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
90153 #define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
90154 typedef __m128 drmp3_f4;
90155 #if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)
90156 #define drmp3_cpuid __cpuid
90157 #else
90158 static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType)
90159 {
90160 #if defined(__PIC__)
90161 __asm__ __volatile__(
90162 #if defined(__x86_64__)
90163 "push %%rbx\n"
90164 "cpuid\n"
90165 "xchgl %%ebx, %1\n"
90166 "pop %%rbx\n"
90167 #else
90168 "xchgl %%ebx, %1\n"
90169 "cpuid\n"
90170 "xchgl %%ebx, %1\n"
90171 #endif
90172 : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
90173 : "a" (InfoType));
90174 #else
90175 __asm__ __volatile__(
90176 "cpuid"
90177 : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
90178 : "a" (InfoType));
90179 #endif
90180 }
90181 #endif
90182 static int drmp3_have_simd(void)
90183 {
90184 #ifdef DR_MP3_ONLY_SIMD
90185 return 1;
90186 #else
90187 static int g_have_simd;
90188 int CPUInfo[4];
90189 #ifdef MINIMP3_TEST
90190 static int g_counter;
90191 if (g_counter++ > 100)
90192 return 0;
90193 #endif
90194 if (g_have_simd)
90195 goto end;
90196 drmp3_cpuid(CPUInfo, 0);
90197 if (CPUInfo[0] > 0)
90198 {
90199 drmp3_cpuid(CPUInfo, 1);
90200 g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;
90201 return g_have_simd - 1;
90202 }
90203 end:
90204 return g_have_simd - 1;
90205 #endif
90206 }
90207 #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
90208 #include <arm_neon.h>
90209 #define DRMP3_HAVE_SSE 0
90210 #define DRMP3_HAVE_SIMD 1
90211 #define DRMP3_VSTORE vst1q_f32
90212 #define DRMP3_VLD vld1q_f32
90213 #define DRMP3_VSET vmovq_n_f32
90214 #define DRMP3_VADD vaddq_f32
90215 #define DRMP3_VSUB vsubq_f32
90216 #define DRMP3_VMUL vmulq_f32
90217 #define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
90218 #define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
90219 #define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
90220 #define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
90221 typedef float32x4_t drmp3_f4;
90222 static int drmp3_have_simd(void)
90223 {
90224 return 1;
90225 }
90226 #else
90227 #define DRMP3_HAVE_SSE 0
90228 #define DRMP3_HAVE_SIMD 0
90229 #ifdef DR_MP3_ONLY_SIMD
90230 #error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
90231 #endif
90232 #endif
90233 #else
90234 #define DRMP3_HAVE_SIMD 0
90235 #endif
90236 #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64)
90237 #define DRMP3_HAVE_ARMV6 1
90238 static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a)
90239 {
90240 drmp3_int32 x = 0;
90241 __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
90242 return x;
90243 }
90244 #else
90245 #define DRMP3_HAVE_ARMV6 0
90246 #endif
90247 #ifndef DRMP3_ASSERT
90248 #include <assert.h>
90249 #define DRMP3_ASSERT(expression) assert(expression)
90250 #endif
90251 #ifndef DRMP3_COPY_MEMORY
90252 #define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
90253 #endif
90254 #ifndef DRMP3_MOVE_MEMORY
90255 #define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
90256 #endif
90257 #ifndef DRMP3_ZERO_MEMORY
90258 #define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
90259 #endif
90260 #define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p)))
90261 #ifndef DRMP3_MALLOC
90262 #define DRMP3_MALLOC(sz) malloc((sz))
90263 #endif
90264 #ifndef DRMP3_REALLOC
90265 #define DRMP3_REALLOC(p, sz) realloc((p), (sz))
90266 #endif
90267 #ifndef DRMP3_FREE
90268 #define DRMP3_FREE(p) free((p))
90269 #endif
90270 typedef struct
90271 {
90272 const drmp3_uint8 *buf;
90273 int pos, limit;
90274 } drmp3_bs;
90275 typedef struct
90276 {
90277 float scf[3*64];
90278 drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
90279 } drmp3_L12_scale_info;
90280 typedef struct
90281 {
90282 drmp3_uint8 tab_offset, code_tab_width, band_count;
90283 } drmp3_L12_subband_alloc;
90284 typedef struct
90285 {
90286 const drmp3_uint8 *sfbtab;
90287 drmp3_uint16 part_23_length, big_values, scalefac_compress;
90288 drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
90289 drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
90290 drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
90291 } drmp3_L3_gr_info;
90292 typedef struct
90293 {
90294 drmp3_bs bs;
90295 drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
90296 drmp3_L3_gr_info gr_info[4];
90297 float grbuf[2][576], scf[40], syn[18 + 15][2*32];
90298 drmp3_uint8 ist_pos[2][39];
90299 } drmp3dec_scratch;
90300 static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
90301 {
90302 bs->buf = data;
90303 bs->pos = 0;
90304 bs->limit = bytes*8;
90305 }
90306 static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n)
90307 {
90308 drmp3_uint32 next, cache = 0, s = bs->pos & 7;
90309 int shl = n + s;
90310 const drmp3_uint8 *p = bs->buf + (bs->pos >> 3);
90311 if ((bs->pos += n) > bs->limit)
90312 return 0;
90313 next = *p++ & (255 >> s);
90314 while ((shl -= 8) > 0)
90315 {
90316 cache |= next << shl;
90317 next = *p++;
90318 }
90319 return cache | (next >> -shl);
90320 }
90321 static int drmp3_hdr_valid(const drmp3_uint8 *h)
90322 {
90323 return h[0] == 0xff &&
90324 ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
90325 (DRMP3_HDR_GET_LAYER(h) != 0) &&
90326 (DRMP3_HDR_GET_BITRATE(h) != 15) &&
90327 (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3);
90328 }
90329 static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2)
90330 {
90331 return drmp3_hdr_valid(h2) &&
90332 ((h1[1] ^ h2[1]) & 0xFE) == 0 &&
90333 ((h1[2] ^ h2[2]) & 0x0C) == 0 &&
90334 !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2));
90335 }
90336 static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h)
90337 {
90338 static const drmp3_uint8 halfrate[2][3][15] = {
90339 { { 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 } },
90340 { { 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 } },
90341 };
90342 return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)];
90343 }
90344 static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h)
90345 {
90346 static const unsigned g_hz[3] = { 44100, 48000, 32000 };
90347 return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h);
90348 }
90349 static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h)
90350 {
90351 return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h));
90352 }
90353 static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size)
90354 {
90355 int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h);
90356 if (DRMP3_HDR_IS_LAYER_1(h))
90357 {
90358 frame_bytes &= ~3;
90359 }
90360 return frame_bytes ? frame_bytes : free_format_size;
90361 }
90362 static int drmp3_hdr_padding(const drmp3_uint8 *h)
90363 {
90364 return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
90365 }
90366 #ifndef DR_MP3_ONLY_MP3
90367 static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci)
90368 {
90369 const drmp3_L12_subband_alloc *alloc;
90370 int mode = DRMP3_HDR_GET_STEREO_MODE(hdr);
90371 int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;
90372 if (DRMP3_HDR_IS_LAYER_1(hdr))
90373 {
90374 static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
90375 alloc = g_alloc_L1;
90376 nbands = 32;
90377 } else if (!DRMP3_HDR_TEST_MPEG1(hdr))
90378 {
90379 static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
90380 alloc = g_alloc_L2M2;
90381 nbands = 30;
90382 } else
90383 {
90384 static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
90385 int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr);
90386 unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO);
90387 if (!kbps)
90388 {
90389 kbps = 192;
90390 }
90391 alloc = g_alloc_L2M1;
90392 nbands = 27;
90393 if (kbps < 56)
90394 {
90395 static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
90396 alloc = g_alloc_L2M1_lowrate;
90397 nbands = sample_rate_idx == 2 ? 12 : 8;
90398 } else if (kbps >= 96 && sample_rate_idx != 1)
90399 {
90400 nbands = 30;
90401 }
90402 }
90403 sci->total_bands = (drmp3_uint8)nbands;
90404 sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands);
90405 return alloc;
90406 }
90407 static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf)
90408 {
90409 static const float g_deq_L12[18*3] = {
90410 #define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
90411 DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9)
90412 };
90413 int i, m;
90414 for (i = 0; i < bands; i++)
90415 {
90416 float s = 0;
90417 int ba = *pba++;
90418 int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
90419 for (m = 4; m; m >>= 1)
90420 {
90421 if (mask & m)
90422 {
90423 int b = drmp3_bs_get_bits(bs, 6);
90424 s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
90425 }
90426 *scf++ = s;
90427 }
90428 }
90429 }
90430 static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci)
90431 {
90432 static const drmp3_uint8 g_bitalloc_code_tab[] = {
90433 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
90434 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
90435 0,17,18, 3,19,4,5,16,
90436 0,17,18,16,
90437 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
90438 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
90439 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
90440 };
90441 const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci);
90442 int i, k = 0, ba_bits = 0;
90443 const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab;
90444 for (i = 0; i < sci->total_bands; i++)
90445 {
90446 drmp3_uint8 ba;
90447 if (i == k)
90448 {
90449 k += subband_alloc->band_count;
90450 ba_bits = subband_alloc->code_tab_width;
90451 ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
90452 subband_alloc++;
90453 }
90454 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
90455 sci->bitalloc[2*i] = ba;
90456 if (i < sci->stereo_bands)
90457 {
90458 ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
90459 }
90460 sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
90461 }
90462 for (i = 0; i < 2*sci->total_bands; i++)
90463 {
90464 sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6);
90465 }
90466 drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
90467 for (i = sci->stereo_bands; i < sci->total_bands; i++)
90468 {
90469 sci->bitalloc[2*i + 1] = 0;
90470 }
90471 }
90472 static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size)
90473 {
90474 int i, j, k, choff = 576;
90475 for (j = 0; j < 4; j++)
90476 {
90477 float *dst = grbuf + group_size*j;
90478 for (i = 0; i < 2*sci->total_bands; i++)
90479 {
90480 int ba = sci->bitalloc[i];
90481 if (ba != 0)
90482 {
90483 if (ba < 17)
90484 {
90485 int half = (1 << (ba - 1)) - 1;
90486 for (k = 0; k < group_size; k++)
90487 {
90488 dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half);
90489 }
90490 } else
90491 {
90492 unsigned mod = (2 << (ba - 17)) + 1;
90493 unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3));
90494 for (k = 0; k < group_size; k++, code /= mod)
90495 {
90496 dst[k] = (float)((int)(code % mod - mod/2));
90497 }
90498 }
90499 }
90500 dst += choff;
90501 choff = 18 - choff;
90502 }
90503 }
90504 return group_size*4;
90505 }
90506 static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst)
90507 {
90508 int i, k;
90509 DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
90510 for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
90511 {
90512 for (k = 0; k < 12; k++)
90513 {
90514 dst[k + 0] *= scf[0];
90515 dst[k + 576] *= scf[3];
90516 }
90517 }
90518 }
90519 #endif
90520 static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
90521 {
90522 static const drmp3_uint8 g_scf_long[8][23] = {
90523 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
90524 { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
90525 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
90526 { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
90527 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
90528 { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
90529 { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
90530 { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
90531 };
90532 static const drmp3_uint8 g_scf_short[8][40] = {
90533 { 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 },
90534 { 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 },
90535 { 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 },
90536 { 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 },
90537 { 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 },
90538 { 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 },
90539 { 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 },
90540 { 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 }
90541 };
90542 static const drmp3_uint8 g_scf_mixed[8][40] = {
90543 { 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 },
90544 { 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 },
90545 { 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 },
90546 { 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 },
90547 { 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 },
90548 { 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 },
90549 { 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 },
90550 { 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 }
90551 };
90552 unsigned tables, scfsi = 0;
90553 int main_data_begin, part_23_sum = 0;
90554 int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
90555 int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
90556 if (DRMP3_HDR_TEST_MPEG1(hdr))
90557 {
90558 gr_count *= 2;
90559 main_data_begin = drmp3_bs_get_bits(bs, 9);
90560 scfsi = drmp3_bs_get_bits(bs, 7 + gr_count);
90561 } else
90562 {
90563 main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
90564 }
90565 do
90566 {
90567 if (DRMP3_HDR_IS_MONO(hdr))
90568 {
90569 scfsi <<= 4;
90570 }
90571 gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12);
90572 part_23_sum += gr->part_23_length;
90573 gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9);
90574 if (gr->big_values > 288)
90575 {
90576 return -1;
90577 }
90578 gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8);
90579 gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
90580 gr->sfbtab = g_scf_long[sr_idx];
90581 gr->n_long_sfb = 22;
90582 gr->n_short_sfb = 0;
90583 if (drmp3_bs_get_bits(bs, 1))
90584 {
90585 gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2);
90586 if (!gr->block_type)
90587 {
90588 return -1;
90589 }
90590 gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
90591 gr->region_count[0] = 7;
90592 gr->region_count[1] = 255;
90593 if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE)
90594 {
90595 scfsi &= 0x0F0F;
90596 if (!gr->mixed_block_flag)
90597 {
90598 gr->region_count[0] = 8;
90599 gr->sfbtab = g_scf_short[sr_idx];
90600 gr->n_long_sfb = 0;
90601 gr->n_short_sfb = 39;
90602 } else
90603 {
90604 gr->sfbtab = g_scf_mixed[sr_idx];
90605 gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
90606 gr->n_short_sfb = 30;
90607 }
90608 }
90609 tables = drmp3_bs_get_bits(bs, 10);
90610 tables <<= 5;
90611 gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
90612 gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
90613 gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
90614 } else
90615 {
90616 gr->block_type = 0;
90617 gr->mixed_block_flag = 0;
90618 tables = drmp3_bs_get_bits(bs, 15);
90619 gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4);
90620 gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
90621 gr->region_count[2] = 255;
90622 }
90623 gr->table_select[0] = (drmp3_uint8)(tables >> 10);
90624 gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31);
90625 gr->table_select[2] = (drmp3_uint8)((tables) & 31);
90626 gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
90627 gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
90628 gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
90629 gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15);
90630 scfsi <<= 4;
90631 gr++;
90632 } while(--gr_count);
90633 if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
90634 {
90635 return -1;
90636 }
90637 return main_data_begin;
90638 }
90639 static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi)
90640 {
90641 int i, k;
90642 for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
90643 {
90644 int cnt = scf_count[i];
90645 if (scfsi & 8)
90646 {
90647 DRMP3_COPY_MEMORY(scf, ist_pos, cnt);
90648 } else
90649 {
90650 int bits = scf_size[i];
90651 if (!bits)
90652 {
90653 DRMP3_ZERO_MEMORY(scf, cnt);
90654 DRMP3_ZERO_MEMORY(ist_pos, cnt);
90655 } else
90656 {
90657 int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
90658 for (k = 0; k < cnt; k++)
90659 {
90660 int s = drmp3_bs_get_bits(bitbuf, bits);
90661 ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s);
90662 scf[k] = (drmp3_uint8)s;
90663 }
90664 }
90665 }
90666 ist_pos += cnt;
90667 scf += cnt;
90668 }
90669 scf[0] = scf[1] = scf[2] = 0;
90670 }
90671 static float drmp3_L3_ldexp_q2(float y, int exp_q2)
90672 {
90673 static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
90674 int e;
90675 do
90676 {
90677 e = DRMP3_MIN(30*4, exp_q2);
90678 y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
90679 } while ((exp_q2 -= e) > 0);
90680 return y;
90681 }
90682 static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch)
90683 {
90684 static const drmp3_uint8 g_scf_partitions[3][28] = {
90685 { 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 },
90686 { 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 },
90687 { 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 }
90688 };
90689 const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
90690 drmp3_uint8 scf_size[4], iscf[40];
90691 int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
90692 float gain;
90693 if (DRMP3_HDR_TEST_MPEG1(hdr))
90694 {
90695 static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
90696 int part = g_scfc_decode[gr->scalefac_compress];
90697 scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2);
90698 scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3);
90699 } else
90700 {
90701 static const drmp3_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 };
90702 int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch;
90703 sfc = gr->scalefac_compress >> ist;
90704 for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
90705 {
90706 for (modprod = 1, i = 3; i >= 0; i--)
90707 {
90708 scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]);
90709 modprod *= g_mod[k + i];
90710 }
90711 }
90712 scf_partition += k;
90713 scfsi = -16;
90714 }
90715 drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
90716 if (gr->n_short_sfb)
90717 {
90718 int sh = 3 - scf_shift;
90719 for (i = 0; i < gr->n_short_sfb; i += 3)
90720 {
90721 iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
90722 iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
90723 iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
90724 }
90725 } else if (gr->preflag)
90726 {
90727 static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
90728 for (i = 0; i < 10; i++)
90729 {
90730 iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]);
90731 }
90732 }
90733 gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
90734 gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp);
90735 for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
90736 {
90737 scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
90738 }
90739 }
90740 static const float g_drmp3_pow43[129 + 16] = {
90741 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,
90742 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
90743 };
90744 static float drmp3_L3_pow_43(int x)
90745 {
90746 float frac;
90747 int sign, mult = 256;
90748 if (x < 129)
90749 {
90750 return g_drmp3_pow43[16 + x];
90751 }
90752 if (x < 1024)
90753 {
90754 mult = 16;
90755 x <<= 3;
90756 }
90757 sign = 2*x & 64;
90758 frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
90759 return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
90760 }
90761 static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
90762 {
90763 static const drmp3_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,
90764 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,
90765 -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,
90766 -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,
90767 -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,
90768 -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,
90769 -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,
90770 -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,
90771 -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,
90772 -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,
90773 -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,
90774 -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,
90775 -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,
90776 -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,
90777 -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,
90778 -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 };
90779 static const drmp3_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};
90780 static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
90781 static const drmp3_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 };
90782 static const drmp3_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 };
90783 #define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n)))
90784 #define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
90785 #define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
90786 #define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
90787 float one = 0.0f;
90788 int ireg = 0, big_val_cnt = gr_info->big_values;
90789 const drmp3_uint8 *sfb = gr_info->sfbtab;
90790 const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
90791 drmp3_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);
90792 int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
90793 bs_next_ptr += 4;
90794 while (big_val_cnt > 0)
90795 {
90796 int tab_num = gr_info->table_select[ireg];
90797 int sfb_cnt = gr_info->region_count[ireg++];
90798 const drmp3_int16 *codebook = tabs + tabindex[tab_num];
90799 int linbits = g_linbits[tab_num];
90800 if (linbits)
90801 {
90802 do
90803 {
90804 np = *sfb++ / 2;
90805 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
90806 one = *scf++;
90807 do
90808 {
90809 int j, w = 5;
90810 int leaf = codebook[DRMP3_PEEK_BITS(w)];
90811 while (leaf < 0)
90812 {
90813 DRMP3_FLUSH_BITS(w);
90814 w = leaf & 7;
90815 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
90816 }
90817 DRMP3_FLUSH_BITS(leaf >> 8);
90818 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
90819 {
90820 int lsb = leaf & 0x0F;
90821 if (lsb == 15)
90822 {
90823 lsb += DRMP3_PEEK_BITS(linbits);
90824 DRMP3_FLUSH_BITS(linbits);
90825 DRMP3_CHECK_BITS;
90826 *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1);
90827 } else
90828 {
90829 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
90830 }
90831 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
90832 }
90833 DRMP3_CHECK_BITS;
90834 } while (--pairs_to_decode);
90835 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
90836 } else
90837 {
90838 do
90839 {
90840 np = *sfb++ / 2;
90841 pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
90842 one = *scf++;
90843 do
90844 {
90845 int j, w = 5;
90846 int leaf = codebook[DRMP3_PEEK_BITS(w)];
90847 while (leaf < 0)
90848 {
90849 DRMP3_FLUSH_BITS(w);
90850 w = leaf & 7;
90851 leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
90852 }
90853 DRMP3_FLUSH_BITS(leaf >> 8);
90854 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
90855 {
90856 int lsb = leaf & 0x0F;
90857 *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
90858 DRMP3_FLUSH_BITS(lsb ? 1 : 0);
90859 }
90860 DRMP3_CHECK_BITS;
90861 } while (--pairs_to_decode);
90862 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
90863 }
90864 }
90865 for (np = 1 - big_val_cnt;; dst += 4)
90866 {
90867 const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
90868 int leaf = codebook_count1[DRMP3_PEEK_BITS(4)];
90869 if (!(leaf & 8))
90870 {
90871 leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
90872 }
90873 DRMP3_FLUSH_BITS(leaf & 7);
90874 if (DRMP3_BSPOS > layer3gr_limit)
90875 {
90876 break;
90877 }
90878 #define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
90879 #define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) }
90880 DRMP3_RELOAD_SCALEFACTOR;
90881 DRMP3_DEQ_COUNT1(0);
90882 DRMP3_DEQ_COUNT1(1);
90883 DRMP3_RELOAD_SCALEFACTOR;
90884 DRMP3_DEQ_COUNT1(2);
90885 DRMP3_DEQ_COUNT1(3);
90886 DRMP3_CHECK_BITS;
90887 }
90888 bs->pos = layer3gr_limit;
90889 }
90890 static void drmp3_L3_midside_stereo(float *left, int n)
90891 {
90892 int i = 0;
90893 float *right = left + 576;
90894 #if DRMP3_HAVE_SIMD
90895 if (drmp3_have_simd())
90896 {
90897 for (; i < n - 3; i += 4)
90898 {
90899 drmp3_f4 vl = DRMP3_VLD(left + i);
90900 drmp3_f4 vr = DRMP3_VLD(right + i);
90901 DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr));
90902 DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr));
90903 }
90904 #ifdef __GNUC__
90905 if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
90906 return;
90907 #endif
90908 }
90909 #endif
90910 for (; i < n; i++)
90911 {
90912 float a = left[i];
90913 float b = right[i];
90914 left[i] = a + b;
90915 right[i] = a - b;
90916 }
90917 }
90918 static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
90919 {
90920 int i;
90921 for (i = 0; i < n; i++)
90922 {
90923 left[i + 576] = left[i]*kr;
90924 left[i] = left[i]*kl;
90925 }
90926 }
90927 static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3])
90928 {
90929 int i, k;
90930 max_band[0] = max_band[1] = max_band[2] = -1;
90931 for (i = 0; i < nbands; i++)
90932 {
90933 for (k = 0; k < sfb[i]; k += 2)
90934 {
90935 if (right[k] != 0 || right[k + 1] != 0)
90936 {
90937 max_band[i % 3] = i;
90938 break;
90939 }
90940 }
90941 right += sfb[i];
90942 }
90943 }
90944 static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh)
90945 {
90946 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 };
90947 unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
90948 for (i = 0; sfb[i]; i++)
90949 {
90950 unsigned ipos = ist_pos[i];
90951 if ((int)i > max_band[i % 3] && ipos < max_pos)
90952 {
90953 float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
90954 if (DRMP3_HDR_TEST_MPEG1(hdr))
90955 {
90956 kl = g_pan[2*ipos];
90957 kr = g_pan[2*ipos + 1];
90958 } else
90959 {
90960 kl = 1;
90961 kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
90962 if (ipos & 1)
90963 {
90964 kl = kr;
90965 kr = 1;
90966 }
90967 }
90968 drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
90969 } else if (DRMP3_HDR_TEST_MS_STEREO(hdr))
90970 {
90971 drmp3_L3_midside_stereo(left, sfb[i]);
90972 }
90973 left += sfb[i];
90974 }
90975 }
90976 static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
90977 {
90978 int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
90979 int i, max_blocks = gr->n_short_sfb ? 3 : 1;
90980 drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
90981 if (gr->n_long_sfb)
90982 {
90983 max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]);
90984 }
90985 for (i = 0; i < max_blocks; i++)
90986 {
90987 int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
90988 int itop = n_sfb - max_blocks + i;
90989 int prev = itop - max_blocks;
90990 ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
90991 }
90992 drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
90993 }
90994 static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb)
90995 {
90996 int i, len;
90997 float *src = grbuf, *dst = scratch;
90998 for (;0 != (len = *sfb); sfb += 3, src += 2*len)
90999 {
91000 for (i = 0; i < len; i++, src++)
91001 {
91002 *dst++ = src[0*len];
91003 *dst++ = src[1*len];
91004 *dst++ = src[2*len];
91005 }
91006 }
91007 DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float));
91008 }
91009 static void drmp3_L3_antialias(float *grbuf, int nbands)
91010 {
91011 static const float g_aa[2][8] = {
91012 {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
91013 {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
91014 };
91015 for (; nbands > 0; nbands--, grbuf += 18)
91016 {
91017 int i = 0;
91018 #if DRMP3_HAVE_SIMD
91019 if (drmp3_have_simd()) for (; i < 8; i += 4)
91020 {
91021 drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i);
91022 drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i);
91023 drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i);
91024 drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i);
91025 vd = DRMP3_VREV(vd);
91026 DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1)));
91027 vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0));
91028 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd));
91029 }
91030 #endif
91031 #ifndef DR_MP3_ONLY_SIMD
91032 for(; i < 8; i++)
91033 {
91034 float u = grbuf[18 + i];
91035 float d = grbuf[17 - i];
91036 grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
91037 grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
91038 }
91039 #endif
91040 }
91041 }
91042 static void drmp3_L3_dct3_9(float *y)
91043 {
91044 float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
91045 s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
91046 t0 = s0 + s6*0.5f;
91047 s0 -= s6;
91048 t4 = (s4 + s2)*0.93969262f;
91049 t2 = (s8 + s2)*0.76604444f;
91050 s6 = (s4 - s8)*0.17364818f;
91051 s4 += s8 - s2;
91052 s2 = s0 - s4*0.5f;
91053 y[4] = s4 + s0;
91054 s8 = t0 - t2 + s6;
91055 s0 = t0 - t4 + t2;
91056 s4 = t0 + t4 - s6;
91057 s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
91058 s3 *= 0.86602540f;
91059 t0 = (s5 + s1)*0.98480775f;
91060 t4 = (s5 - s7)*0.34202014f;
91061 t2 = (s1 + s7)*0.64278761f;
91062 s1 = (s1 - s5 - s7)*0.86602540f;
91063 s5 = t0 - s3 - t2;
91064 s7 = t4 - s3 - t0;
91065 s3 = t4 + s3 - t2;
91066 y[0] = s4 - s7;
91067 y[1] = s2 + s1;
91068 y[2] = s0 - s3;
91069 y[3] = s8 + s5;
91070 y[5] = s8 - s5;
91071 y[6] = s0 + s3;
91072 y[7] = s2 - s1;
91073 y[8] = s4 + s7;
91074 }
91075 static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
91076 {
91077 int i, j;
91078 static const float g_twid9[18] = {
91079 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
91080 };
91081 for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
91082 {
91083 float co[9], si[9];
91084 co[0] = -grbuf[0];
91085 si[0] = grbuf[17];
91086 for (i = 0; i < 4; i++)
91087 {
91088 si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
91089 co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
91090 si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
91091 co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
91092 }
91093 drmp3_L3_dct3_9(co);
91094 drmp3_L3_dct3_9(si);
91095 si[1] = -si[1];
91096 si[3] = -si[3];
91097 si[5] = -si[5];
91098 si[7] = -si[7];
91099 i = 0;
91100 #if DRMP3_HAVE_SIMD
91101 if (drmp3_have_simd()) for (; i < 8; i += 4)
91102 {
91103 drmp3_f4 vovl = DRMP3_VLD(overlap + i);
91104 drmp3_f4 vc = DRMP3_VLD(co + i);
91105 drmp3_f4 vs = DRMP3_VLD(si + i);
91106 drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i);
91107 drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i);
91108 drmp3_f4 vw0 = DRMP3_VLD(window + i);
91109 drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i);
91110 drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0));
91111 DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1)));
91112 DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1)));
91113 vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0));
91114 DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum));
91115 }
91116 #endif
91117 for (; i < 9; i++)
91118 {
91119 float ovl = overlap[i];
91120 float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
91121 overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
91122 grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
91123 grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
91124 }
91125 }
91126 }
91127 static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst)
91128 {
91129 float m1 = x1*0.86602540f;
91130 float a1 = x0 - x2*0.5f;
91131 dst[1] = x0 + x2;
91132 dst[0] = a1 + m1;
91133 dst[2] = a1 - m1;
91134 }
91135 static void drmp3_L3_imdct12(float *x, float *dst, float *overlap)
91136 {
91137 static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
91138 float co[3], si[3];
91139 int i;
91140 drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
91141 drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
91142 si[1] = -si[1];
91143 for (i = 0; i < 3; i++)
91144 {
91145 float ovl = overlap[i];
91146 float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
91147 overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
91148 dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
91149 dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
91150 }
91151 }
91152 static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
91153 {
91154 for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
91155 {
91156 float tmp[18];
91157 DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp));
91158 DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float));
91159 drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
91160 drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
91161 drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
91162 }
91163 }
91164 static void drmp3_L3_change_sign(float *grbuf)
91165 {
91166 int b, i;
91167 for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
91168 for (i = 1; i < 18; i += 2)
91169 grbuf[i] = -grbuf[i];
91170 }
91171 static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
91172 {
91173 static const float g_mdct_window[2][18] = {
91174 { 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 },
91175 { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
91176 };
91177 if (n_long_bands)
91178 {
91179 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
91180 grbuf += 18*n_long_bands;
91181 overlap += 9*n_long_bands;
91182 }
91183 if (block_type == DRMP3_SHORT_BLOCK_TYPE)
91184 drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
91185 else
91186 drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
91187 }
91188 static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s)
91189 {
91190 int pos = (s->bs.pos + 7)/8u;
91191 int remains = s->bs.limit/8u - pos;
91192 if (remains > DRMP3_MAX_BITRESERVOIR_BYTES)
91193 {
91194 pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES;
91195 remains = DRMP3_MAX_BITRESERVOIR_BYTES;
91196 }
91197 if (remains > 0)
91198 {
91199 DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains);
91200 }
91201 h->reserv = remains;
91202 }
91203 static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin)
91204 {
91205 int frame_bytes = (bs->limit - bs->pos)/8;
91206 int bytes_have = DRMP3_MIN(h->reserv, main_data_begin);
91207 DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin));
91208 DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
91209 drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
91210 return h->reserv >= main_data_begin;
91211 }
91212 static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch)
91213 {
91214 int ch;
91215 for (ch = 0; ch < nch; ch++)
91216 {
91217 int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
91218 drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
91219 drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
91220 }
91221 if (DRMP3_HDR_TEST_I_STEREO(h->header))
91222 {
91223 drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
91224 } else if (DRMP3_HDR_IS_MS_STEREO(h->header))
91225 {
91226 drmp3_L3_midside_stereo(s->grbuf[0], 576);
91227 }
91228 for (ch = 0; ch < nch; ch++, gr_info++)
91229 {
91230 int aa_bands = 31;
91231 int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
91232 if (gr_info->n_short_sfb)
91233 {
91234 aa_bands = n_long_bands - 1;
91235 drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
91236 }
91237 drmp3_L3_antialias(s->grbuf[ch], aa_bands);
91238 drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
91239 drmp3_L3_change_sign(s->grbuf[ch]);
91240 }
91241 }
91242 static void drmp3d_DCT_II(float *grbuf, int n)
91243 {
91244 static const float g_sec[24] = {
91245 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
91246 };
91247 int i, k = 0;
91248 #if DRMP3_HAVE_SIMD
91249 if (drmp3_have_simd()) for (; k < n; k += 4)
91250 {
91251 drmp3_f4 t[4][8], *x;
91252 float *y = grbuf + k;
91253 for (x = t[0], i = 0; i < 8; i++, x++)
91254 {
91255 drmp3_f4 x0 = DRMP3_VLD(&y[i*18]);
91256 drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]);
91257 drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]);
91258 drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]);
91259 drmp3_f4 t0 = DRMP3_VADD(x0, x3);
91260 drmp3_f4 t1 = DRMP3_VADD(x1, x2);
91261 drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]);
91262 drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]);
91263 x[0] = DRMP3_VADD(t0, t1);
91264 x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]);
91265 x[16] = DRMP3_VADD(t3, t2);
91266 x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]);
91267 }
91268 for (x = t[0], i = 0; i < 4; i++, x += 8)
91269 {
91270 drmp3_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;
91271 xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7);
91272 x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6);
91273 x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5);
91274 x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4);
91275 x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3);
91276 x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2);
91277 x[0] = DRMP3_VADD(x0, x1);
91278 x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f);
91279 x5 = DRMP3_VADD(x5, x6);
91280 x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f);
91281 x7 = DRMP3_VADD(x7, xt);
91282 x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f);
91283 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
91284 x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f));
91285 x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
91286 x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6);
91287 x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f);
91288 x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f);
91289 x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f);
91290 x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f);
91291 x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f);
91292 x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f);
91293 }
91294 if (k > n - 3)
91295 {
91296 #if DRMP3_HAVE_SSE
91297 #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
91298 #else
91299 #define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v))
91300 #endif
91301 for (i = 0; i < 7; i++, y += 4*18)
91302 {
91303 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
91304 DRMP3_VSAVE2(0, t[0][i]);
91305 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s));
91306 DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
91307 DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s));
91308 }
91309 DRMP3_VSAVE2(0, t[0][7]);
91310 DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7]));
91311 DRMP3_VSAVE2(2, t[1][7]);
91312 DRMP3_VSAVE2(3, t[3][7]);
91313 } else
91314 {
91315 #define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v)
91316 for (i = 0; i < 7; i++, y += 4*18)
91317 {
91318 drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
91319 DRMP3_VSAVE4(0, t[0][i]);
91320 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s));
91321 DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
91322 DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s));
91323 }
91324 DRMP3_VSAVE4(0, t[0][7]);
91325 DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7]));
91326 DRMP3_VSAVE4(2, t[1][7]);
91327 DRMP3_VSAVE4(3, t[3][7]);
91328 }
91329 } else
91330 #endif
91331 #ifdef DR_MP3_ONLY_SIMD
91332 {}
91333 #else
91334 for (; k < n; k++)
91335 {
91336 float t[4][8], *x, *y = grbuf + k;
91337 for (x = t[0], i = 0; i < 8; i++, x++)
91338 {
91339 float x0 = y[i*18];
91340 float x1 = y[(15 - i)*18];
91341 float x2 = y[(16 + i)*18];
91342 float x3 = y[(31 - i)*18];
91343 float t0 = x0 + x3;
91344 float t1 = x1 + x2;
91345 float t2 = (x1 - x2)*g_sec[3*i + 0];
91346 float t3 = (x0 - x3)*g_sec[3*i + 1];
91347 x[0] = t0 + t1;
91348 x[8] = (t0 - t1)*g_sec[3*i + 2];
91349 x[16] = t3 + t2;
91350 x[24] = (t3 - t2)*g_sec[3*i + 2];
91351 }
91352 for (x = t[0], i = 0; i < 4; i++, x += 8)
91353 {
91354 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;
91355 xt = x0 - x7; x0 += x7;
91356 x7 = x1 - x6; x1 += x6;
91357 x6 = x2 - x5; x2 += x5;
91358 x5 = x3 - x4; x3 += x4;
91359 x4 = x0 - x3; x0 += x3;
91360 x3 = x1 - x2; x1 += x2;
91361 x[0] = x0 + x1;
91362 x[4] = (x0 - x1)*0.70710677f;
91363 x5 = x5 + x6;
91364 x6 = (x6 + x7)*0.70710677f;
91365 x7 = x7 + xt;
91366 x3 = (x3 + x4)*0.70710677f;
91367 x5 -= x7*0.198912367f;
91368 x7 += x5*0.382683432f;
91369 x5 -= x7*0.198912367f;
91370 x0 = xt - x6; xt += x6;
91371 x[1] = (xt + x7)*0.50979561f;
91372 x[2] = (x4 + x3)*0.54119611f;
91373 x[3] = (x0 - x5)*0.60134488f;
91374 x[5] = (x0 + x5)*0.89997619f;
91375 x[6] = (x4 - x3)*1.30656302f;
91376 x[7] = (xt - x7)*2.56291556f;
91377 }
91378 for (i = 0; i < 7; i++, y += 4*18)
91379 {
91380 y[0*18] = t[0][i];
91381 y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
91382 y[2*18] = t[1][i] + t[1][i + 1];
91383 y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
91384 }
91385 y[0*18] = t[0][7];
91386 y[1*18] = t[2][7] + t[3][7];
91387 y[2*18] = t[1][7];
91388 y[3*18] = t[3][7];
91389 }
91390 #endif
91391 }
91392 #ifndef DR_MP3_FLOAT_OUTPUT
91393 typedef drmp3_int16 drmp3d_sample_t;
91394 static drmp3_int16 drmp3d_scale_pcm(float sample)
91395 {
91396 drmp3_int16 s;
91397 #if DRMP3_HAVE_ARMV6
91398 drmp3_int32 s32 = (drmp3_int32)(sample + .5f);
91399 s32 -= (s32 < 0);
91400 s = (drmp3_int16)drmp3_clip_int16_arm(s32);
91401 #else
91402 if (sample >= 32766.5) return (drmp3_int16) 32767;
91403 if (sample <= -32767.5) return (drmp3_int16)-32768;
91404 s = (drmp3_int16)(sample + .5f);
91405 s -= (s < 0);
91406 #endif
91407 return s;
91408 }
91409 #else
91410 typedef float drmp3d_sample_t;
91411 static float drmp3d_scale_pcm(float sample)
91412 {
91413 return sample*(1.f/32768.f);
91414 }
91415 #endif
91416 static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
91417 {
91418 float a;
91419 a = (z[14*64] - z[ 0]) * 29;
91420 a += (z[ 1*64] + z[13*64]) * 213;
91421 a += (z[12*64] - z[ 2*64]) * 459;
91422 a += (z[ 3*64] + z[11*64]) * 2037;
91423 a += (z[10*64] - z[ 4*64]) * 5153;
91424 a += (z[ 5*64] + z[ 9*64]) * 6574;
91425 a += (z[ 8*64] - z[ 6*64]) * 37489;
91426 a += z[ 7*64] * 75038;
91427 pcm[0] = drmp3d_scale_pcm(a);
91428 z += 2;
91429 a = z[14*64] * 104;
91430 a += z[12*64] * 1567;
91431 a += z[10*64] * 9727;
91432 a += z[ 8*64] * 64019;
91433 a += z[ 6*64] * -9975;
91434 a += z[ 4*64] * -45;
91435 a += z[ 2*64] * 146;
91436 a += z[ 0*64] * -5;
91437 pcm[16*nch] = drmp3d_scale_pcm(a);
91438 }
91439 static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
91440 {
91441 int i;
91442 float *xr = xl + 576*(nch - 1);
91443 drmp3d_sample_t *dstr = dstl + (nch - 1);
91444 static const float g_win[] = {
91445 -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
91446 -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
91447 -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
91448 -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
91449 -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
91450 -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
91451 -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
91452 -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
91453 -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
91454 -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
91455 -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
91456 -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
91457 -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
91458 -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
91459 -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
91460 };
91461 float *zlin = lins + 15*64;
91462 const float *w = g_win;
91463 zlin[4*15] = xl[18*16];
91464 zlin[4*15 + 1] = xr[18*16];
91465 zlin[4*15 + 2] = xl[0];
91466 zlin[4*15 + 3] = xr[0];
91467 zlin[4*31] = xl[1 + 18*16];
91468 zlin[4*31 + 1] = xr[1 + 18*16];
91469 zlin[4*31 + 2] = xl[1];
91470 zlin[4*31 + 3] = xr[1];
91471 drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
91472 drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
91473 drmp3d_synth_pair(dstl, nch, lins + 4*15);
91474 drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
91475 #if DRMP3_HAVE_SIMD
91476 if (drmp3_have_simd()) for (i = 14; i >= 0; i--)
91477 {
91478 #define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]);
91479 #define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); }
91480 #define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); }
91481 #define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); }
91482 drmp3_f4 a, b;
91483 zlin[4*i] = xl[18*(31 - i)];
91484 zlin[4*i + 1] = xr[18*(31 - i)];
91485 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
91486 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
91487 zlin[4*i + 64] = xl[1 + 18*(1 + i)];
91488 zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
91489 zlin[4*i - 64 + 2] = xl[18*(1 + i)];
91490 zlin[4*i - 64 + 3] = xr[18*(1 + i)];
91491 DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
91492 {
91493 #ifndef DR_MP3_FLOAT_OUTPUT
91494 #if DRMP3_HAVE_SSE
91495 static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
91496 static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
91497 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
91498 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
91499 dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
91500 dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
91501 dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
91502 dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
91503 dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
91504 dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
91505 dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
91506 dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
91507 #else
91508 int16x4_t pcma, pcmb;
91509 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
91510 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
91511 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
91512 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
91513 vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
91514 vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
91515 vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
91516 vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
91517 vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
91518 vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
91519 vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
91520 vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
91521 #endif
91522 #else
91523 #if DRMP3_HAVE_SSE
91524 static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
91525 #else
91526 const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);
91527 #endif
91528 a = DRMP3_VMUL(a, g_scale);
91529 b = DRMP3_VMUL(b, g_scale);
91530 #if DRMP3_HAVE_SSE
91531 _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
91532 _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
91533 _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
91534 _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
91535 _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
91536 _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
91537 _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
91538 _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
91539 #else
91540 vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
91541 vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
91542 vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
91543 vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
91544 vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
91545 vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
91546 vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
91547 vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
91548 #endif
91549 #endif
91550 }
91551 } else
91552 #endif
91553 #ifdef DR_MP3_ONLY_SIMD
91554 {}
91555 #else
91556 for (i = 14; i >= 0; i--)
91557 {
91558 #define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64];
91559 #define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; }
91560 #define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; }
91561 #define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; }
91562 float a[4], b[4];
91563 zlin[4*i] = xl[18*(31 - i)];
91564 zlin[4*i + 1] = xr[18*(31 - i)];
91565 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
91566 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
91567 zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
91568 zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
91569 zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
91570 zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
91571 DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7)
91572 dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]);
91573 dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]);
91574 dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]);
91575 dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]);
91576 dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]);
91577 dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]);
91578 dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]);
91579 dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]);
91580 }
91581 #endif
91582 }
91583 static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins)
91584 {
91585 int i;
91586 for (i = 0; i < nch; i++)
91587 {
91588 drmp3d_DCT_II(grbuf + 576*i, nbands);
91589 }
91590 DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64);
91591 for (i = 0; i < nbands; i += 2)
91592 {
91593 drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
91594 }
91595 #ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL
91596 if (nch == 1)
91597 {
91598 for (i = 0; i < 15*64; i += 2)
91599 {
91600 qmf_state[i] = lins[nbands*64 + i];
91601 }
91602 } else
91603 #endif
91604 {
91605 DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64);
91606 }
91607 }
91608 static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes)
91609 {
91610 int i, nmatch;
91611 for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
91612 {
91613 i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i);
91614 if (i + DRMP3_HDR_SIZE > mp3_bytes)
91615 return nmatch > 0;
91616 if (!drmp3_hdr_compare(hdr, hdr + i))
91617 return 0;
91618 }
91619 return 1;
91620 }
91621 static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
91622 {
91623 int i, k;
91624 for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++)
91625 {
91626 if (drmp3_hdr_valid(mp3))
91627 {
91628 int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes);
91629 int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3);
91630 for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++)
91631 {
91632 if (drmp3_hdr_compare(mp3, mp3 + k))
91633 {
91634 int fb = k - drmp3_hdr_padding(mp3);
91635 int nextfb = fb + drmp3_hdr_padding(mp3 + k);
91636 if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb))
91637 continue;
91638 frame_and_padding = k;
91639 frame_bytes = fb;
91640 *free_format_bytes = fb;
91641 }
91642 }
91643 if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
91644 drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
91645 (!i && frame_and_padding == mp3_bytes))
91646 {
91647 *ptr_frame_bytes = frame_and_padding;
91648 return i;
91649 }
91650 *free_format_bytes = 0;
91651 }
91652 }
91653 *ptr_frame_bytes = 0;
91654 return mp3_bytes;
91655 }
91656 DRMP3_API void drmp3dec_init(drmp3dec *dec)
91657 {
91658 dec->header[0] = 0;
91659 }
91660 DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
91661 {
91662 int i = 0, igr, frame_size = 0, success = 1;
91663 const drmp3_uint8 *hdr;
91664 drmp3_bs bs_frame[1];
91665 drmp3dec_scratch scratch;
91666 if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3))
91667 {
91668 frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3);
91669 if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size)))
91670 {
91671 frame_size = 0;
91672 }
91673 }
91674 if (!frame_size)
91675 {
91676 DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec));
91677 i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
91678 if (!frame_size || i + frame_size > mp3_bytes)
91679 {
91680 info->frame_bytes = i;
91681 return 0;
91682 }
91683 }
91684 hdr = mp3 + i;
91685 DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE);
91686 info->frame_bytes = i + frame_size;
91687 info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
91688 info->hz = drmp3_hdr_sample_rate_hz(hdr);
91689 info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
91690 info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
91691 drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
91692 if (DRMP3_HDR_IS_CRC(hdr))
91693 {
91694 drmp3_bs_get_bits(bs_frame, 16);
91695 }
91696 if (info->layer == 3)
91697 {
91698 int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
91699 if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
91700 {
91701 drmp3dec_init(dec);
91702 return 0;
91703 }
91704 success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
91705 if (success && pcm != NULL)
91706 {
91707 for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
91708 {
91709 DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
91710 drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
91711 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
91712 }
91713 }
91714 drmp3_L3_save_reservoir(dec, &scratch);
91715 } else
91716 {
91717 #ifdef DR_MP3_ONLY_MP3
91718 return 0;
91719 #else
91720 drmp3_L12_scale_info sci[1];
91721 if (pcm == NULL) {
91722 return drmp3_hdr_frame_samples(hdr);
91723 }
91724 drmp3_L12_read_scale_info(hdr, bs_frame, sci);
91725 DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
91726 for (i = 0, igr = 0; igr < 3; igr++)
91727 {
91728 if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
91729 {
91730 i = 0;
91731 drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
91732 drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
91733 DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
91734 pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
91735 }
91736 if (bs_frame->pos > bs_frame->limit)
91737 {
91738 drmp3dec_init(dec);
91739 return 0;
91740 }
91741 }
91742 #endif
91743 }
91744 return success*drmp3_hdr_frame_samples(dec->header);
91745 }
91746 DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples)
91747 {
91748 size_t i = 0;
91749 #if DRMP3_HAVE_SIMD
91750 size_t aligned_count = num_samples & ~7;
91751 for(; i < aligned_count; i+=8)
91752 {
91753 drmp3_f4 scale = DRMP3_VSET(32768.0f);
91754 drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale);
91755 drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale);
91756 #if DRMP3_HAVE_SSE
91757 drmp3_f4 s16max = DRMP3_VSET( 32767.0f);
91758 drmp3_f4 s16min = DRMP3_VSET(-32768.0f);
91759 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
91760 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
91761 out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
91762 out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
91763 out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
91764 out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
91765 out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
91766 out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
91767 out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
91768 out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
91769 #else
91770 int16x4_t pcma, pcmb;
91771 a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
91772 b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
91773 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
91774 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
91775 vst1_lane_s16(out+i , pcma, 0);
91776 vst1_lane_s16(out+i+1, pcma, 1);
91777 vst1_lane_s16(out+i+2, pcma, 2);
91778 vst1_lane_s16(out+i+3, pcma, 3);
91779 vst1_lane_s16(out+i+4, pcmb, 0);
91780 vst1_lane_s16(out+i+5, pcmb, 1);
91781 vst1_lane_s16(out+i+6, pcmb, 2);
91782 vst1_lane_s16(out+i+7, pcmb, 3);
91783 #endif
91784 }
91785 #endif
91786 for(; i < num_samples; i++)
91787 {
91788 float sample = in[i] * 32768.0f;
91789 if (sample >= 32766.5)
91790 out[i] = (drmp3_int16) 32767;
91791 else if (sample <= -32767.5)
91792 out[i] = (drmp3_int16)-32768;
91793 else
91794 {
91795 short s = (drmp3_int16)(sample + .5f);
91796 s -= (s < 0);
91797 out[i] = s;
91798 }
91799 }
91800 }
91801 #if defined(SIZE_MAX)
91802 #define DRMP3_SIZE_MAX SIZE_MAX
91803 #else
91804 #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
91805 #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF)
91806 #else
91807 #define DRMP3_SIZE_MAX 0xFFFFFFFF
91808 #endif
91809 #endif
91810 #ifndef DRMP3_SEEK_LEADING_MP3_FRAMES
91811 #define DRMP3_SEEK_LEADING_MP3_FRAMES 2
91812 #endif
91813 #define DRMP3_MIN_DATA_CHUNK_SIZE 16384
91814 #ifndef DRMP3_DATA_CHUNK_SIZE
91815 #define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4)
91816 #endif
91817 #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
91818 #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi)))
91819 #ifndef DRMP3_PI_D
91820 #define DRMP3_PI_D 3.14159265358979323846264
91821 #endif
91822 #define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2
91823 static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a)
91824 {
91825 return x*(1-a) + y*a;
91826 }
91827 static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a)
91828 {
91829 float r0 = (y - x);
91830 float r1 = r0*a;
91831 return x + r1;
91832 }
91833 static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b)
91834 {
91835 for (;;) {
91836 if (b == 0) {
91837 break;
91838 } else {
91839 drmp3_uint32 t = a;
91840 a = b;
91841 b = t % a;
91842 }
91843 }
91844 return a;
91845 }
91846 static void* drmp3__malloc_default(size_t sz, void* pUserData)
91847 {
91848 (void)pUserData;
91849 return DRMP3_MALLOC(sz);
91850 }
91851 static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData)
91852 {
91853 (void)pUserData;
91854 return DRMP3_REALLOC(p, sz);
91855 }
91856 static void drmp3__free_default(void* p, void* pUserData)
91857 {
91858 (void)pUserData;
91859 DRMP3_FREE(p);
91860 }
91861 static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
91862 {
91863 if (pAllocationCallbacks == NULL) {
91864 return NULL;
91865 }
91866 if (pAllocationCallbacks->onMalloc != NULL) {
91867 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
91868 }
91869 if (pAllocationCallbacks->onRealloc != NULL) {
91870 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
91871 }
91872 return NULL;
91873 }
91874 static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks)
91875 {
91876 if (pAllocationCallbacks == NULL) {
91877 return NULL;
91878 }
91879 if (pAllocationCallbacks->onRealloc != NULL) {
91880 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
91881 }
91882 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
91883 void* p2;
91884 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
91885 if (p2 == NULL) {
91886 return NULL;
91887 }
91888 if (p != NULL) {
91889 DRMP3_COPY_MEMORY(p2, p, szOld);
91890 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
91891 }
91892 return p2;
91893 }
91894 return NULL;
91895 }
91896 static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
91897 {
91898 if (p == NULL || pAllocationCallbacks == NULL) {
91899 return;
91900 }
91901 if (pAllocationCallbacks->onFree != NULL) {
91902 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
91903 }
91904 }
91905 static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks)
91906 {
91907 if (pAllocationCallbacks != NULL) {
91908 return *pAllocationCallbacks;
91909 } else {
91910 drmp3_allocation_callbacks allocationCallbacks;
91911 allocationCallbacks.pUserData = NULL;
91912 allocationCallbacks.onMalloc = drmp3__malloc_default;
91913 allocationCallbacks.onRealloc = drmp3__realloc_default;
91914 allocationCallbacks.onFree = drmp3__free_default;
91915 return allocationCallbacks;
91916 }
91917 }
91918 static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
91919 {
91920 size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
91921 pMP3->streamCursor += bytesRead;
91922 return bytesRead;
91923 }
91924 static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
91925 {
91926 DRMP3_ASSERT(offset >= 0);
91927 if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
91928 return DRMP3_FALSE;
91929 }
91930 if (origin == drmp3_seek_origin_start) {
91931 pMP3->streamCursor = (drmp3_uint64)offset;
91932 } else {
91933 pMP3->streamCursor += offset;
91934 }
91935 return DRMP3_TRUE;
91936 }
91937 static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin)
91938 {
91939 if (offset <= 0x7FFFFFFF) {
91940 return drmp3__on_seek(pMP3, (int)offset, origin);
91941 }
91942 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
91943 return DRMP3_FALSE;
91944 }
91945 offset -= 0x7FFFFFFF;
91946 while (offset > 0) {
91947 if (offset <= 0x7FFFFFFF) {
91948 if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
91949 return DRMP3_FALSE;
91950 }
91951 offset = 0;
91952 } else {
91953 if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
91954 return DRMP3_FALSE;
91955 }
91956 offset -= 0x7FFFFFFF;
91957 }
91958 }
91959 return DRMP3_TRUE;
91960 }
91961 static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
91962 {
91963 drmp3_uint32 pcmFramesRead = 0;
91964 DRMP3_ASSERT(pMP3 != NULL);
91965 DRMP3_ASSERT(pMP3->onRead != NULL);
91966 if (pMP3->atEnd) {
91967 return 0;
91968 }
91969 for (;;) {
91970 drmp3dec_frame_info info;
91971 if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) {
91972 size_t bytesRead;
91973 if (pMP3->pData != NULL) {
91974 DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
91975 }
91976 pMP3->dataConsumed = 0;
91977 if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
91978 drmp3_uint8* pNewData;
91979 size_t newDataCap;
91980 newDataCap = DRMP3_DATA_CHUNK_SIZE;
91981 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
91982 if (pNewData == NULL) {
91983 return 0;
91984 }
91985 pMP3->pData = pNewData;
91986 pMP3->dataCapacity = newDataCap;
91987 }
91988 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
91989 if (bytesRead == 0) {
91990 if (pMP3->dataSize == 0) {
91991 pMP3->atEnd = DRMP3_TRUE;
91992 return 0;
91993 }
91994 }
91995 pMP3->dataSize += bytesRead;
91996 }
91997 if (pMP3->dataSize > INT_MAX) {
91998 pMP3->atEnd = DRMP3_TRUE;
91999 return 0;
92000 }
92001 DRMP3_ASSERT(pMP3->pData != NULL);
92002 DRMP3_ASSERT(pMP3->dataCapacity > 0);
92003 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);
92004 if (info.frame_bytes > 0) {
92005 pMP3->dataConsumed += (size_t)info.frame_bytes;
92006 pMP3->dataSize -= (size_t)info.frame_bytes;
92007 }
92008 if (pcmFramesRead > 0) {
92009 pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
92010 pMP3->pcmFramesConsumedInMP3Frame = 0;
92011 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
92012 pMP3->mp3FrameChannels = info.channels;
92013 pMP3->mp3FrameSampleRate = info.hz;
92014 break;
92015 } else if (info.frame_bytes == 0) {
92016 size_t bytesRead;
92017 DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
92018 pMP3->dataConsumed = 0;
92019 if (pMP3->dataCapacity == pMP3->dataSize) {
92020 drmp3_uint8* pNewData;
92021 size_t newDataCap;
92022 newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE;
92023 pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
92024 if (pNewData == NULL) {
92025 return 0;
92026 }
92027 pMP3->pData = pNewData;
92028 pMP3->dataCapacity = newDataCap;
92029 }
92030 bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
92031 if (bytesRead == 0) {
92032 pMP3->atEnd = DRMP3_TRUE;
92033 return 0;
92034 }
92035 pMP3->dataSize += bytesRead;
92036 }
92037 };
92038 return pcmFramesRead;
92039 }
92040 static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
92041 {
92042 drmp3_uint32 pcmFramesRead = 0;
92043 drmp3dec_frame_info info;
92044 DRMP3_ASSERT(pMP3 != NULL);
92045 DRMP3_ASSERT(pMP3->memory.pData != NULL);
92046 if (pMP3->atEnd) {
92047 return 0;
92048 }
92049 for (;;) {
92050 pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
92051 if (pcmFramesRead > 0) {
92052 pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
92053 pMP3->pcmFramesConsumedInMP3Frame = 0;
92054 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
92055 pMP3->mp3FrameChannels = info.channels;
92056 pMP3->mp3FrameSampleRate = info.hz;
92057 break;
92058 } else if (info.frame_bytes > 0) {
92059 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
92060 } else {
92061 break;
92062 }
92063 }
92064 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
92065 return pcmFramesRead;
92066 }
92067 static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
92068 {
92069 if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
92070 return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);
92071 } else {
92072 return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);
92073 }
92074 }
92075 static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
92076 {
92077 DRMP3_ASSERT(pMP3 != NULL);
92078 return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
92079 }
92080 #if 0
92081 static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
92082 {
92083 drmp3_uint32 pcmFrameCount;
92084 DRMP3_ASSERT(pMP3 != NULL);
92085 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
92086 if (pcmFrameCount == 0) {
92087 return 0;
92088 }
92089 pMP3->currentPCMFrame += pcmFrameCount;
92090 pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
92091 pMP3->pcmFramesRemainingInMP3Frame = 0;
92092 return pcmFrameCount;
92093 }
92094 #endif
92095 static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
92096 {
92097 DRMP3_ASSERT(pMP3 != NULL);
92098 DRMP3_ASSERT(onRead != NULL);
92099 drmp3dec_init(&pMP3->decoder);
92100 pMP3->onRead = onRead;
92101 pMP3->onSeek = onSeek;
92102 pMP3->pUserData = pUserData;
92103 pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
92104 if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
92105 return DRMP3_FALSE;
92106 }
92107 if (drmp3_decode_next_frame(pMP3) == 0) {
92108 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
92109 return DRMP3_FALSE;
92110 }
92111 pMP3->channels = pMP3->mp3FrameChannels;
92112 pMP3->sampleRate = pMP3->mp3FrameSampleRate;
92113 return DRMP3_TRUE;
92114 }
92115 DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
92116 {
92117 if (pMP3 == NULL || onRead == NULL) {
92118 return DRMP3_FALSE;
92119 }
92120 DRMP3_ZERO_OBJECT(pMP3);
92121 return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
92122 }
92123 static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
92124 {
92125 drmp3* pMP3 = (drmp3*)pUserData;
92126 size_t bytesRemaining;
92127 DRMP3_ASSERT(pMP3 != NULL);
92128 DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
92129 bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
92130 if (bytesToRead > bytesRemaining) {
92131 bytesToRead = bytesRemaining;
92132 }
92133 if (bytesToRead > 0) {
92134 DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
92135 pMP3->memory.currentReadPos += bytesToRead;
92136 }
92137 return bytesToRead;
92138 }
92139 static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
92140 {
92141 drmp3* pMP3 = (drmp3*)pUserData;
92142 DRMP3_ASSERT(pMP3 != NULL);
92143 if (origin == drmp3_seek_origin_current) {
92144 if (byteOffset > 0) {
92145 if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
92146 byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);
92147 }
92148 } else {
92149 if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
92150 byteOffset = -(int)pMP3->memory.currentReadPos;
92151 }
92152 }
92153 pMP3->memory.currentReadPos += byteOffset;
92154 } else {
92155 if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
92156 pMP3->memory.currentReadPos = byteOffset;
92157 } else {
92158 pMP3->memory.currentReadPos = pMP3->memory.dataSize;
92159 }
92160 }
92161 return DRMP3_TRUE;
92162 }
92163 DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks)
92164 {
92165 if (pMP3 == NULL) {
92166 return DRMP3_FALSE;
92167 }
92168 DRMP3_ZERO_OBJECT(pMP3);
92169 if (pData == NULL || dataSize == 0) {
92170 return DRMP3_FALSE;
92171 }
92172 pMP3->memory.pData = (const drmp3_uint8*)pData;
92173 pMP3->memory.dataSize = dataSize;
92174 pMP3->memory.currentReadPos = 0;
92175 return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks);
92176 }
92177 #ifndef DR_MP3_NO_STDIO
92178 #include <stdio.h>
92179 #include <wchar.h>
92180 #include <errno.h>
92181 static drmp3_result drmp3_result_from_errno(int e)
92182 {
92183 switch (e)
92184 {
92185 case 0: return DRMP3_SUCCESS;
92186 #ifdef EPERM
92187 case EPERM: return DRMP3_INVALID_OPERATION;
92188 #endif
92189 #ifdef ENOENT
92190 case ENOENT: return DRMP3_DOES_NOT_EXIST;
92191 #endif
92192 #ifdef ESRCH
92193 case ESRCH: return DRMP3_DOES_NOT_EXIST;
92194 #endif
92195 #ifdef EINTR
92196 case EINTR: return DRMP3_INTERRUPT;
92197 #endif
92198 #ifdef EIO
92199 case EIO: return DRMP3_IO_ERROR;
92200 #endif
92201 #ifdef ENXIO
92202 case ENXIO: return DRMP3_DOES_NOT_EXIST;
92203 #endif
92204 #ifdef E2BIG
92205 case E2BIG: return DRMP3_INVALID_ARGS;
92206 #endif
92207 #ifdef ENOEXEC
92208 case ENOEXEC: return DRMP3_INVALID_FILE;
92209 #endif
92210 #ifdef EBADF
92211 case EBADF: return DRMP3_INVALID_FILE;
92212 #endif
92213 #ifdef ECHILD
92214 case ECHILD: return DRMP3_ERROR;
92215 #endif
92216 #ifdef EAGAIN
92217 case EAGAIN: return DRMP3_UNAVAILABLE;
92218 #endif
92219 #ifdef ENOMEM
92220 case ENOMEM: return DRMP3_OUT_OF_MEMORY;
92221 #endif
92222 #ifdef EACCES
92223 case EACCES: return DRMP3_ACCESS_DENIED;
92224 #endif
92225 #ifdef EFAULT
92226 case EFAULT: return DRMP3_BAD_ADDRESS;
92227 #endif
92228 #ifdef ENOTBLK
92229 case ENOTBLK: return DRMP3_ERROR;
92230 #endif
92231 #ifdef EBUSY
92232 case EBUSY: return DRMP3_BUSY;
92233 #endif
92234 #ifdef EEXIST
92235 case EEXIST: return DRMP3_ALREADY_EXISTS;
92236 #endif
92237 #ifdef EXDEV
92238 case EXDEV: return DRMP3_ERROR;
92239 #endif
92240 #ifdef ENODEV
92241 case ENODEV: return DRMP3_DOES_NOT_EXIST;
92242 #endif
92243 #ifdef ENOTDIR
92244 case ENOTDIR: return DRMP3_NOT_DIRECTORY;
92245 #endif
92246 #ifdef EISDIR
92247 case EISDIR: return DRMP3_IS_DIRECTORY;
92248 #endif
92249 #ifdef EINVAL
92250 case EINVAL: return DRMP3_INVALID_ARGS;
92251 #endif
92252 #ifdef ENFILE
92253 case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES;
92254 #endif
92255 #ifdef EMFILE
92256 case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES;
92257 #endif
92258 #ifdef ENOTTY
92259 case ENOTTY: return DRMP3_INVALID_OPERATION;
92260 #endif
92261 #ifdef ETXTBSY
92262 case ETXTBSY: return DRMP3_BUSY;
92263 #endif
92264 #ifdef EFBIG
92265 case EFBIG: return DRMP3_TOO_BIG;
92266 #endif
92267 #ifdef ENOSPC
92268 case ENOSPC: return DRMP3_NO_SPACE;
92269 #endif
92270 #ifdef ESPIPE
92271 case ESPIPE: return DRMP3_BAD_SEEK;
92272 #endif
92273 #ifdef EROFS
92274 case EROFS: return DRMP3_ACCESS_DENIED;
92275 #endif
92276 #ifdef EMLINK
92277 case EMLINK: return DRMP3_TOO_MANY_LINKS;
92278 #endif
92279 #ifdef EPIPE
92280 case EPIPE: return DRMP3_BAD_PIPE;
92281 #endif
92282 #ifdef EDOM
92283 case EDOM: return DRMP3_OUT_OF_RANGE;
92284 #endif
92285 #ifdef ERANGE
92286 case ERANGE: return DRMP3_OUT_OF_RANGE;
92287 #endif
92288 #ifdef EDEADLK
92289 case EDEADLK: return DRMP3_DEADLOCK;
92290 #endif
92291 #ifdef ENAMETOOLONG
92292 case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG;
92293 #endif
92294 #ifdef ENOLCK
92295 case ENOLCK: return DRMP3_ERROR;
92296 #endif
92297 #ifdef ENOSYS
92298 case ENOSYS: return DRMP3_NOT_IMPLEMENTED;
92299 #endif
92300 #ifdef ENOTEMPTY
92301 case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY;
92302 #endif
92303 #ifdef ELOOP
92304 case ELOOP: return DRMP3_TOO_MANY_LINKS;
92305 #endif
92306 #ifdef ENOMSG
92307 case ENOMSG: return DRMP3_NO_MESSAGE;
92308 #endif
92309 #ifdef EIDRM
92310 case EIDRM: return DRMP3_ERROR;
92311 #endif
92312 #ifdef ECHRNG
92313 case ECHRNG: return DRMP3_ERROR;
92314 #endif
92315 #ifdef EL2NSYNC
92316 case EL2NSYNC: return DRMP3_ERROR;
92317 #endif
92318 #ifdef EL3HLT
92319 case EL3HLT: return DRMP3_ERROR;
92320 #endif
92321 #ifdef EL3RST
92322 case EL3RST: return DRMP3_ERROR;
92323 #endif
92324 #ifdef ELNRNG
92325 case ELNRNG: return DRMP3_OUT_OF_RANGE;
92326 #endif
92327 #ifdef EUNATCH
92328 case EUNATCH: return DRMP3_ERROR;
92329 #endif
92330 #ifdef ENOCSI
92331 case ENOCSI: return DRMP3_ERROR;
92332 #endif
92333 #ifdef EL2HLT
92334 case EL2HLT: return DRMP3_ERROR;
92335 #endif
92336 #ifdef EBADE
92337 case EBADE: return DRMP3_ERROR;
92338 #endif
92339 #ifdef EBADR
92340 case EBADR: return DRMP3_ERROR;
92341 #endif
92342 #ifdef EXFULL
92343 case EXFULL: return DRMP3_ERROR;
92344 #endif
92345 #ifdef ENOANO
92346 case ENOANO: return DRMP3_ERROR;
92347 #endif
92348 #ifdef EBADRQC
92349 case EBADRQC: return DRMP3_ERROR;
92350 #endif
92351 #ifdef EBADSLT
92352 case EBADSLT: return DRMP3_ERROR;
92353 #endif
92354 #ifdef EBFONT
92355 case EBFONT: return DRMP3_INVALID_FILE;
92356 #endif
92357 #ifdef ENOSTR
92358 case ENOSTR: return DRMP3_ERROR;
92359 #endif
92360 #ifdef ENODATA
92361 case ENODATA: return DRMP3_NO_DATA_AVAILABLE;
92362 #endif
92363 #ifdef ETIME
92364 case ETIME: return DRMP3_TIMEOUT;
92365 #endif
92366 #ifdef ENOSR
92367 case ENOSR: return DRMP3_NO_DATA_AVAILABLE;
92368 #endif
92369 #ifdef ENONET
92370 case ENONET: return DRMP3_NO_NETWORK;
92371 #endif
92372 #ifdef ENOPKG
92373 case ENOPKG: return DRMP3_ERROR;
92374 #endif
92375 #ifdef EREMOTE
92376 case EREMOTE: return DRMP3_ERROR;
92377 #endif
92378 #ifdef ENOLINK
92379 case ENOLINK: return DRMP3_ERROR;
92380 #endif
92381 #ifdef EADV
92382 case EADV: return DRMP3_ERROR;
92383 #endif
92384 #ifdef ESRMNT
92385 case ESRMNT: return DRMP3_ERROR;
92386 #endif
92387 #ifdef ECOMM
92388 case ECOMM: return DRMP3_ERROR;
92389 #endif
92390 #ifdef EPROTO
92391 case EPROTO: return DRMP3_ERROR;
92392 #endif
92393 #ifdef EMULTIHOP
92394 case EMULTIHOP: return DRMP3_ERROR;
92395 #endif
92396 #ifdef EDOTDOT
92397 case EDOTDOT: return DRMP3_ERROR;
92398 #endif
92399 #ifdef EBADMSG
92400 case EBADMSG: return DRMP3_BAD_MESSAGE;
92401 #endif
92402 #ifdef EOVERFLOW
92403 case EOVERFLOW: return DRMP3_TOO_BIG;
92404 #endif
92405 #ifdef ENOTUNIQ
92406 case ENOTUNIQ: return DRMP3_NOT_UNIQUE;
92407 #endif
92408 #ifdef EBADFD
92409 case EBADFD: return DRMP3_ERROR;
92410 #endif
92411 #ifdef EREMCHG
92412 case EREMCHG: return DRMP3_ERROR;
92413 #endif
92414 #ifdef ELIBACC
92415 case ELIBACC: return DRMP3_ACCESS_DENIED;
92416 #endif
92417 #ifdef ELIBBAD
92418 case ELIBBAD: return DRMP3_INVALID_FILE;
92419 #endif
92420 #ifdef ELIBSCN
92421 case ELIBSCN: return DRMP3_INVALID_FILE;
92422 #endif
92423 #ifdef ELIBMAX
92424 case ELIBMAX: return DRMP3_ERROR;
92425 #endif
92426 #ifdef ELIBEXEC
92427 case ELIBEXEC: return DRMP3_ERROR;
92428 #endif
92429 #ifdef EILSEQ
92430 case EILSEQ: return DRMP3_INVALID_DATA;
92431 #endif
92432 #ifdef ERESTART
92433 case ERESTART: return DRMP3_ERROR;
92434 #endif
92435 #ifdef ESTRPIPE
92436 case ESTRPIPE: return DRMP3_ERROR;
92437 #endif
92438 #ifdef EUSERS
92439 case EUSERS: return DRMP3_ERROR;
92440 #endif
92441 #ifdef ENOTSOCK
92442 case ENOTSOCK: return DRMP3_NOT_SOCKET;
92443 #endif
92444 #ifdef EDESTADDRREQ
92445 case EDESTADDRREQ: return DRMP3_NO_ADDRESS;
92446 #endif
92447 #ifdef EMSGSIZE
92448 case EMSGSIZE: return DRMP3_TOO_BIG;
92449 #endif
92450 #ifdef EPROTOTYPE
92451 case EPROTOTYPE: return DRMP3_BAD_PROTOCOL;
92452 #endif
92453 #ifdef ENOPROTOOPT
92454 case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE;
92455 #endif
92456 #ifdef EPROTONOSUPPORT
92457 case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED;
92458 #endif
92459 #ifdef ESOCKTNOSUPPORT
92460 case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED;
92461 #endif
92462 #ifdef EOPNOTSUPP
92463 case EOPNOTSUPP: return DRMP3_INVALID_OPERATION;
92464 #endif
92465 #ifdef EPFNOSUPPORT
92466 case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED;
92467 #endif
92468 #ifdef EAFNOSUPPORT
92469 case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED;
92470 #endif
92471 #ifdef EADDRINUSE
92472 case EADDRINUSE: return DRMP3_ALREADY_IN_USE;
92473 #endif
92474 #ifdef EADDRNOTAVAIL
92475 case EADDRNOTAVAIL: return DRMP3_ERROR;
92476 #endif
92477 #ifdef ENETDOWN
92478 case ENETDOWN: return DRMP3_NO_NETWORK;
92479 #endif
92480 #ifdef ENETUNREACH
92481 case ENETUNREACH: return DRMP3_NO_NETWORK;
92482 #endif
92483 #ifdef ENETRESET
92484 case ENETRESET: return DRMP3_NO_NETWORK;
92485 #endif
92486 #ifdef ECONNABORTED
92487 case ECONNABORTED: return DRMP3_NO_NETWORK;
92488 #endif
92489 #ifdef ECONNRESET
92490 case ECONNRESET: return DRMP3_CONNECTION_RESET;
92491 #endif
92492 #ifdef ENOBUFS
92493 case ENOBUFS: return DRMP3_NO_SPACE;
92494 #endif
92495 #ifdef EISCONN
92496 case EISCONN: return DRMP3_ALREADY_CONNECTED;
92497 #endif
92498 #ifdef ENOTCONN
92499 case ENOTCONN: return DRMP3_NOT_CONNECTED;
92500 #endif
92501 #ifdef ESHUTDOWN
92502 case ESHUTDOWN: return DRMP3_ERROR;
92503 #endif
92504 #ifdef ETOOMANYREFS
92505 case ETOOMANYREFS: return DRMP3_ERROR;
92506 #endif
92507 #ifdef ETIMEDOUT
92508 case ETIMEDOUT: return DRMP3_TIMEOUT;
92509 #endif
92510 #ifdef ECONNREFUSED
92511 case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED;
92512 #endif
92513 #ifdef EHOSTDOWN
92514 case EHOSTDOWN: return DRMP3_NO_HOST;
92515 #endif
92516 #ifdef EHOSTUNREACH
92517 case EHOSTUNREACH: return DRMP3_NO_HOST;
92518 #endif
92519 #ifdef EALREADY
92520 case EALREADY: return DRMP3_IN_PROGRESS;
92521 #endif
92522 #ifdef EINPROGRESS
92523 case EINPROGRESS: return DRMP3_IN_PROGRESS;
92524 #endif
92525 #ifdef ESTALE
92526 case ESTALE: return DRMP3_INVALID_FILE;
92527 #endif
92528 #ifdef EUCLEAN
92529 case EUCLEAN: return DRMP3_ERROR;
92530 #endif
92531 #ifdef ENOTNAM
92532 case ENOTNAM: return DRMP3_ERROR;
92533 #endif
92534 #ifdef ENAVAIL
92535 case ENAVAIL: return DRMP3_ERROR;
92536 #endif
92537 #ifdef EISNAM
92538 case EISNAM: return DRMP3_ERROR;
92539 #endif
92540 #ifdef EREMOTEIO
92541 case EREMOTEIO: return DRMP3_IO_ERROR;
92542 #endif
92543 #ifdef EDQUOT
92544 case EDQUOT: return DRMP3_NO_SPACE;
92545 #endif
92546 #ifdef ENOMEDIUM
92547 case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST;
92548 #endif
92549 #ifdef EMEDIUMTYPE
92550 case EMEDIUMTYPE: return DRMP3_ERROR;
92551 #endif
92552 #ifdef ECANCELED
92553 case ECANCELED: return DRMP3_CANCELLED;
92554 #endif
92555 #ifdef ENOKEY
92556 case ENOKEY: return DRMP3_ERROR;
92557 #endif
92558 #ifdef EKEYEXPIRED
92559 case EKEYEXPIRED: return DRMP3_ERROR;
92560 #endif
92561 #ifdef EKEYREVOKED
92562 case EKEYREVOKED: return DRMP3_ERROR;
92563 #endif
92564 #ifdef EKEYREJECTED
92565 case EKEYREJECTED: return DRMP3_ERROR;
92566 #endif
92567 #ifdef EOWNERDEAD
92568 case EOWNERDEAD: return DRMP3_ERROR;
92569 #endif
92570 #ifdef ENOTRECOVERABLE
92571 case ENOTRECOVERABLE: return DRMP3_ERROR;
92572 #endif
92573 #ifdef ERFKILL
92574 case ERFKILL: return DRMP3_ERROR;
92575 #endif
92576 #ifdef EHWPOISON
92577 case EHWPOISON: return DRMP3_ERROR;
92578 #endif
92579 default: return DRMP3_ERROR;
92580 }
92581 }
92582 static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
92583 {
92584 #if defined(_MSC_VER) && _MSC_VER >= 1400
92585 errno_t err;
92586 #endif
92587 if (ppFile != NULL) {
92588 *ppFile = NULL;
92589 }
92590 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
92591 return DRMP3_INVALID_ARGS;
92592 }
92593 #if defined(_MSC_VER) && _MSC_VER >= 1400
92594 err = fopen_s(ppFile, pFilePath, pOpenMode);
92595 if (err != 0) {
92596 return drmp3_result_from_errno(err);
92597 }
92598 #else
92599 #if defined(_WIN32) || defined(__APPLE__)
92600 *ppFile = fopen(pFilePath, pOpenMode);
92601 #else
92602 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
92603 *ppFile = fopen64(pFilePath, pOpenMode);
92604 #else
92605 *ppFile = fopen(pFilePath, pOpenMode);
92606 #endif
92607 #endif
92608 if (*ppFile == NULL) {
92609 drmp3_result result = drmp3_result_from_errno(errno);
92610 if (result == DRMP3_SUCCESS) {
92611 result = DRMP3_ERROR;
92612 }
92613 return result;
92614 }
92615 #endif
92616 return DRMP3_SUCCESS;
92617 }
92618 #if defined(_WIN32)
92619 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
92620 #define DRMP3_HAS_WFOPEN
92621 #endif
92622 #endif
92623 static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks)
92624 {
92625 if (ppFile != NULL) {
92626 *ppFile = NULL;
92627 }
92628 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
92629 return DRMP3_INVALID_ARGS;
92630 }
92631 #if defined(DRMP3_HAS_WFOPEN)
92632 {
92633 #if defined(_MSC_VER) && _MSC_VER >= 1400
92634 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
92635 if (err != 0) {
92636 return drmp3_result_from_errno(err);
92637 }
92638 #else
92639 *ppFile = _wfopen(pFilePath, pOpenMode);
92640 if (*ppFile == NULL) {
92641 return drmp3_result_from_errno(errno);
92642 }
92643 #endif
92644 (void)pAllocationCallbacks;
92645 }
92646 #else
92647 #if defined(__DJGPP__)
92648 {
92649 }
92650 #else
92651 {
92652 mbstate_t mbs;
92653 size_t lenMB;
92654 const wchar_t* pFilePathTemp = pFilePath;
92655 char* pFilePathMB = NULL;
92656 char pOpenModeMB[32] = {0};
92657 DRMP3_ZERO_OBJECT(&mbs);
92658 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
92659 if (lenMB == (size_t)-1) {
92660 return drmp3_result_from_errno(errno);
92661 }
92662 pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
92663 if (pFilePathMB == NULL) {
92664 return DRMP3_OUT_OF_MEMORY;
92665 }
92666 pFilePathTemp = pFilePath;
92667 DRMP3_ZERO_OBJECT(&mbs);
92668 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
92669 {
92670 size_t i = 0;
92671 for (;;) {
92672 if (pOpenMode[i] == 0) {
92673 pOpenModeMB[i] = '\0';
92674 break;
92675 }
92676 pOpenModeMB[i] = (char)pOpenMode[i];
92677 i += 1;
92678 }
92679 }
92680 *ppFile = fopen(pFilePathMB, pOpenModeMB);
92681 drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
92682 }
92683 #endif
92684 if (*ppFile == NULL) {
92685 return DRMP3_ERROR;
92686 }
92687 #endif
92688 return DRMP3_SUCCESS;
92689 }
92690 static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
92691 {
92692 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
92693 }
92694 static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
92695 {
92696 return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
92697 }
92698 DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
92699 {
92700 drmp3_bool32 result;
92701 FILE* pFile;
92702 if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
92703 return DRMP3_FALSE;
92704 }
92705 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
92706 if (result != DRMP3_TRUE) {
92707 fclose(pFile);
92708 return result;
92709 }
92710 return DRMP3_TRUE;
92711 }
92712 DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
92713 {
92714 drmp3_bool32 result;
92715 FILE* pFile;
92716 if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
92717 return DRMP3_FALSE;
92718 }
92719 result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
92720 if (result != DRMP3_TRUE) {
92721 fclose(pFile);
92722 return result;
92723 }
92724 return DRMP3_TRUE;
92725 }
92726 #endif
92727 DRMP3_API void drmp3_uninit(drmp3* pMP3)
92728 {
92729 if (pMP3 == NULL) {
92730 return;
92731 }
92732 #ifndef DR_MP3_NO_STDIO
92733 if (pMP3->onRead == drmp3__on_read_stdio) {
92734 FILE* pFile = (FILE*)pMP3->pUserData;
92735 if (pFile != NULL) {
92736 fclose(pFile);
92737 pMP3->pUserData = NULL;
92738 }
92739 }
92740 #endif
92741 drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
92742 }
92743 #if defined(DR_MP3_FLOAT_OUTPUT)
92744 static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount)
92745 {
92746 drmp3_uint64 i;
92747 drmp3_uint64 i4;
92748 drmp3_uint64 sampleCount4;
92749 i = 0;
92750 sampleCount4 = sampleCount >> 2;
92751 for (i4 = 0; i4 < sampleCount4; i4 += 1) {
92752 float x0 = src[i+0];
92753 float x1 = src[i+1];
92754 float x2 = src[i+2];
92755 float x3 = src[i+3];
92756 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
92757 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
92758 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
92759 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
92760 x0 = x0 * 32767.0f;
92761 x1 = x1 * 32767.0f;
92762 x2 = x2 * 32767.0f;
92763 x3 = x3 * 32767.0f;
92764 dst[i+0] = (drmp3_int16)x0;
92765 dst[i+1] = (drmp3_int16)x1;
92766 dst[i+2] = (drmp3_int16)x2;
92767 dst[i+3] = (drmp3_int16)x3;
92768 i += 4;
92769 }
92770 for (; i < sampleCount; i += 1) {
92771 float x = src[i];
92772 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
92773 x = x * 32767.0f;
92774 dst[i] = (drmp3_int16)x;
92775 }
92776 }
92777 #endif
92778 #if !defined(DR_MP3_FLOAT_OUTPUT)
92779 static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount)
92780 {
92781 drmp3_uint64 i;
92782 for (i = 0; i < sampleCount; i += 1) {
92783 float x = (float)src[i];
92784 x = x * 0.000030517578125f;
92785 dst[i] = x;
92786 }
92787 }
92788 #endif
92789 static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut)
92790 {
92791 drmp3_uint64 totalFramesRead = 0;
92792 DRMP3_ASSERT(pMP3 != NULL);
92793 DRMP3_ASSERT(pMP3->onRead != NULL);
92794 while (framesToRead > 0) {
92795 drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
92796 if (pBufferOut != NULL) {
92797 #if defined(DR_MP3_FLOAT_OUTPUT)
92798 float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
92799 float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
92800 DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
92801 #else
92802 drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels);
92803 drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
92804 DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
92805 #endif
92806 }
92807 pMP3->currentPCMFrame += framesToConsume;
92808 pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
92809 pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
92810 totalFramesRead += framesToConsume;
92811 framesToRead -= framesToConsume;
92812 if (framesToRead == 0) {
92813 break;
92814 }
92815 DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
92816 if (drmp3_decode_next_frame(pMP3) == 0) {
92817 break;
92818 }
92819 }
92820 return totalFramesRead;
92821 }
92822 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
92823 {
92824 if (pMP3 == NULL || pMP3->onRead == NULL) {
92825 return 0;
92826 }
92827 #if defined(DR_MP3_FLOAT_OUTPUT)
92828 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
92829 #else
92830 {
92831 drmp3_int16 pTempS16[8192];
92832 drmp3_uint64 totalPCMFramesRead = 0;
92833 while (totalPCMFramesRead < framesToRead) {
92834 drmp3_uint64 framesJustRead;
92835 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
92836 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
92837 if (framesToReadNow > framesRemaining) {
92838 framesToReadNow = framesRemaining;
92839 }
92840 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
92841 if (framesJustRead == 0) {
92842 break;
92843 }
92844 drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
92845 totalPCMFramesRead += framesJustRead;
92846 }
92847 return totalPCMFramesRead;
92848 }
92849 #endif
92850 }
92851 DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
92852 {
92853 if (pMP3 == NULL || pMP3->onRead == NULL) {
92854 return 0;
92855 }
92856 #if !defined(DR_MP3_FLOAT_OUTPUT)
92857 return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
92858 #else
92859 {
92860 float pTempF32[4096];
92861 drmp3_uint64 totalPCMFramesRead = 0;
92862 while (totalPCMFramesRead < framesToRead) {
92863 drmp3_uint64 framesJustRead;
92864 drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
92865 drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels;
92866 if (framesToReadNow > framesRemaining) {
92867 framesToReadNow = framesRemaining;
92868 }
92869 framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
92870 if (framesJustRead == 0) {
92871 break;
92872 }
92873 drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
92874 totalPCMFramesRead += framesJustRead;
92875 }
92876 return totalPCMFramesRead;
92877 }
92878 #endif
92879 }
92880 static void drmp3_reset(drmp3* pMP3)
92881 {
92882 DRMP3_ASSERT(pMP3 != NULL);
92883 pMP3->pcmFramesConsumedInMP3Frame = 0;
92884 pMP3->pcmFramesRemainingInMP3Frame = 0;
92885 pMP3->currentPCMFrame = 0;
92886 pMP3->dataSize = 0;
92887 pMP3->atEnd = DRMP3_FALSE;
92888 drmp3dec_init(&pMP3->decoder);
92889 }
92890 static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
92891 {
92892 DRMP3_ASSERT(pMP3 != NULL);
92893 DRMP3_ASSERT(pMP3->onSeek != NULL);
92894 if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
92895 return DRMP3_FALSE;
92896 }
92897 drmp3_reset(pMP3);
92898 return DRMP3_TRUE;
92899 }
92900 static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
92901 {
92902 drmp3_uint64 framesRead;
92903 #if defined(DR_MP3_FLOAT_OUTPUT)
92904 framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
92905 #else
92906 framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
92907 #endif
92908 if (framesRead != frameOffset) {
92909 return DRMP3_FALSE;
92910 }
92911 return DRMP3_TRUE;
92912 }
92913 static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
92914 {
92915 DRMP3_ASSERT(pMP3 != NULL);
92916 if (frameIndex == pMP3->currentPCMFrame) {
92917 return DRMP3_TRUE;
92918 }
92919 if (frameIndex < pMP3->currentPCMFrame) {
92920 if (!drmp3_seek_to_start_of_stream(pMP3)) {
92921 return DRMP3_FALSE;
92922 }
92923 }
92924 DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
92925 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
92926 }
92927 static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
92928 {
92929 drmp3_uint32 iSeekPoint;
92930 DRMP3_ASSERT(pSeekPointIndex != NULL);
92931 *pSeekPointIndex = 0;
92932 if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
92933 return DRMP3_FALSE;
92934 }
92935 for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
92936 if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
92937 break;
92938 }
92939 *pSeekPointIndex = iSeekPoint;
92940 }
92941 return DRMP3_TRUE;
92942 }
92943 static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
92944 {
92945 drmp3_seek_point seekPoint;
92946 drmp3_uint32 priorSeekPointIndex;
92947 drmp3_uint16 iMP3Frame;
92948 drmp3_uint64 leftoverFrames;
92949 DRMP3_ASSERT(pMP3 != NULL);
92950 DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
92951 DRMP3_ASSERT(pMP3->seekPointCount > 0);
92952 if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
92953 seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
92954 } else {
92955 seekPoint.seekPosInBytes = 0;
92956 seekPoint.pcmFrameIndex = 0;
92957 seekPoint.mp3FramesToDiscard = 0;
92958 seekPoint.pcmFramesToDiscard = 0;
92959 }
92960 if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
92961 return DRMP3_FALSE;
92962 }
92963 drmp3_reset(pMP3);
92964 for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
92965 drmp3_uint32 pcmFramesRead;
92966 drmp3d_sample_t* pPCMFrames;
92967 pPCMFrames = NULL;
92968 if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
92969 pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames;
92970 }
92971 pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames);
92972 if (pcmFramesRead == 0) {
92973 return DRMP3_FALSE;
92974 }
92975 }
92976 pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
92977 leftoverFrames = frameIndex - pMP3->currentPCMFrame;
92978 return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
92979 }
92980 DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
92981 {
92982 if (pMP3 == NULL || pMP3->onSeek == NULL) {
92983 return DRMP3_FALSE;
92984 }
92985 if (frameIndex == 0) {
92986 return drmp3_seek_to_start_of_stream(pMP3);
92987 }
92988 if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
92989 return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
92990 } else {
92991 return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
92992 }
92993 }
92994 DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount)
92995 {
92996 drmp3_uint64 currentPCMFrame;
92997 drmp3_uint64 totalPCMFrameCount;
92998 drmp3_uint64 totalMP3FrameCount;
92999 if (pMP3 == NULL) {
93000 return DRMP3_FALSE;
93001 }
93002 if (pMP3->onSeek == NULL) {
93003 return DRMP3_FALSE;
93004 }
93005 currentPCMFrame = pMP3->currentPCMFrame;
93006 if (!drmp3_seek_to_start_of_stream(pMP3)) {
93007 return DRMP3_FALSE;
93008 }
93009 totalPCMFrameCount = 0;
93010 totalMP3FrameCount = 0;
93011 for (;;) {
93012 drmp3_uint32 pcmFramesInCurrentMP3Frame;
93013 pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL);
93014 if (pcmFramesInCurrentMP3Frame == 0) {
93015 break;
93016 }
93017 totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
93018 totalMP3FrameCount += 1;
93019 }
93020 if (!drmp3_seek_to_start_of_stream(pMP3)) {
93021 return DRMP3_FALSE;
93022 }
93023 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
93024 return DRMP3_FALSE;
93025 }
93026 if (pMP3FrameCount != NULL) {
93027 *pMP3FrameCount = totalMP3FrameCount;
93028 }
93029 if (pPCMFrameCount != NULL) {
93030 *pPCMFrameCount = totalPCMFrameCount;
93031 }
93032 return DRMP3_TRUE;
93033 }
93034 DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
93035 {
93036 drmp3_uint64 totalPCMFrameCount;
93037 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
93038 return 0;
93039 }
93040 return totalPCMFrameCount;
93041 }
93042 DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
93043 {
93044 drmp3_uint64 totalMP3FrameCount;
93045 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
93046 return 0;
93047 }
93048 return totalMP3FrameCount;
93049 }
93050 static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
93051 {
93052 float srcRatio;
93053 float pcmFrameCountOutF;
93054 drmp3_uint32 pcmFrameCountOut;
93055 srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
93056 DRMP3_ASSERT(srcRatio > 0);
93057 pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
93058 pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF;
93059 *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
93060 *pRunningPCMFrameCount += pcmFrameCountOut;
93061 }
93062 typedef struct
93063 {
93064 drmp3_uint64 bytePos;
93065 drmp3_uint64 pcmFrameIndex;
93066 } drmp3__seeking_mp3_frame_info;
93067 DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints)
93068 {
93069 drmp3_uint32 seekPointCount;
93070 drmp3_uint64 currentPCMFrame;
93071 drmp3_uint64 totalMP3FrameCount;
93072 drmp3_uint64 totalPCMFrameCount;
93073 if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
93074 return DRMP3_FALSE;
93075 }
93076 seekPointCount = *pSeekPointCount;
93077 if (seekPointCount == 0) {
93078 return DRMP3_FALSE;
93079 }
93080 currentPCMFrame = pMP3->currentPCMFrame;
93081 if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
93082 return DRMP3_FALSE;
93083 }
93084 if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) {
93085 seekPointCount = 1;
93086 pSeekPoints[0].seekPosInBytes = 0;
93087 pSeekPoints[0].pcmFrameIndex = 0;
93088 pSeekPoints[0].mp3FramesToDiscard = 0;
93089 pSeekPoints[0].pcmFramesToDiscard = 0;
93090 } else {
93091 drmp3_uint64 pcmFramesBetweenSeekPoints;
93092 drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1];
93093 drmp3_uint64 runningPCMFrameCount = 0;
93094 float runningPCMFrameCountFractionalPart = 0;
93095 drmp3_uint64 nextTargetPCMFrame;
93096 drmp3_uint32 iMP3Frame;
93097 drmp3_uint32 iSeekPoint;
93098 if (seekPointCount > totalMP3FrameCount-1) {
93099 seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
93100 }
93101 pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
93102 if (!drmp3_seek_to_start_of_stream(pMP3)) {
93103 return DRMP3_FALSE;
93104 }
93105 for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
93106 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
93107 DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
93108 mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
93109 mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
93110 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
93111 if (pcmFramesInCurrentMP3FrameIn == 0) {
93112 return DRMP3_FALSE;
93113 }
93114 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
93115 }
93116 nextTargetPCMFrame = 0;
93117 for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
93118 nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
93119 for (;;) {
93120 if (nextTargetPCMFrame < runningPCMFrameCount) {
93121 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
93122 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
93123 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
93124 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
93125 break;
93126 } else {
93127 size_t i;
93128 drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
93129 for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) {
93130 mp3FrameInfo[i] = mp3FrameInfo[i+1];
93131 }
93132 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
93133 mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
93134 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
93135 if (pcmFramesInCurrentMP3FrameIn == 0) {
93136 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
93137 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
93138 pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
93139 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
93140 break;
93141 }
93142 drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
93143 }
93144 }
93145 }
93146 if (!drmp3_seek_to_start_of_stream(pMP3)) {
93147 return DRMP3_FALSE;
93148 }
93149 if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
93150 return DRMP3_FALSE;
93151 }
93152 }
93153 *pSeekPointCount = seekPointCount;
93154 return DRMP3_TRUE;
93155 }
93156 DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints)
93157 {
93158 if (pMP3 == NULL) {
93159 return DRMP3_FALSE;
93160 }
93161 if (seekPointCount == 0 || pSeekPoints == NULL) {
93162 pMP3->seekPointCount = 0;
93163 pMP3->pSeekPoints = NULL;
93164 } else {
93165 pMP3->seekPointCount = seekPointCount;
93166 pMP3->pSeekPoints = pSeekPoints;
93167 }
93168 return DRMP3_TRUE;
93169 }
93170 static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
93171 {
93172 drmp3_uint64 totalFramesRead = 0;
93173 drmp3_uint64 framesCapacity = 0;
93174 float* pFrames = NULL;
93175 float temp[4096];
93176 DRMP3_ASSERT(pMP3 != NULL);
93177 for (;;) {
93178 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
93179 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
93180 if (framesJustRead == 0) {
93181 break;
93182 }
93183 if (framesCapacity < totalFramesRead + framesJustRead) {
93184 drmp3_uint64 oldFramesBufferSize;
93185 drmp3_uint64 newFramesBufferSize;
93186 drmp3_uint64 newFramesCap;
93187 float* pNewFrames;
93188 newFramesCap = framesCapacity * 2;
93189 if (newFramesCap < totalFramesRead + framesJustRead) {
93190 newFramesCap = totalFramesRead + framesJustRead;
93191 }
93192 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
93193 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
93194 if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) {
93195 break;
93196 }
93197 pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
93198 if (pNewFrames == NULL) {
93199 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
93200 break;
93201 }
93202 pFrames = pNewFrames;
93203 framesCapacity = newFramesCap;
93204 }
93205 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
93206 totalFramesRead += framesJustRead;
93207 if (framesJustRead != framesToReadRightNow) {
93208 break;
93209 }
93210 }
93211 if (pConfig != NULL) {
93212 pConfig->channels = pMP3->channels;
93213 pConfig->sampleRate = pMP3->sampleRate;
93214 }
93215 drmp3_uninit(pMP3);
93216 if (pTotalFrameCount) {
93217 *pTotalFrameCount = totalFramesRead;
93218 }
93219 return pFrames;
93220 }
93221 static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
93222 {
93223 drmp3_uint64 totalFramesRead = 0;
93224 drmp3_uint64 framesCapacity = 0;
93225 drmp3_int16* pFrames = NULL;
93226 drmp3_int16 temp[4096];
93227 DRMP3_ASSERT(pMP3 != NULL);
93228 for (;;) {
93229 drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
93230 drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
93231 if (framesJustRead == 0) {
93232 break;
93233 }
93234 if (framesCapacity < totalFramesRead + framesJustRead) {
93235 drmp3_uint64 newFramesBufferSize;
93236 drmp3_uint64 oldFramesBufferSize;
93237 drmp3_uint64 newFramesCap;
93238 drmp3_int16* pNewFrames;
93239 newFramesCap = framesCapacity * 2;
93240 if (newFramesCap < totalFramesRead + framesJustRead) {
93241 newFramesCap = totalFramesRead + framesJustRead;
93242 }
93243 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16);
93244 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16);
93245 if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) {
93246 break;
93247 }
93248 pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
93249 if (pNewFrames == NULL) {
93250 drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
93251 break;
93252 }
93253 pFrames = pNewFrames;
93254 framesCapacity = newFramesCap;
93255 }
93256 DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16)));
93257 totalFramesRead += framesJustRead;
93258 if (framesJustRead != framesToReadRightNow) {
93259 break;
93260 }
93261 }
93262 if (pConfig != NULL) {
93263 pConfig->channels = pMP3->channels;
93264 pConfig->sampleRate = pMP3->sampleRate;
93265 }
93266 drmp3_uninit(pMP3);
93267 if (pTotalFrameCount) {
93268 *pTotalFrameCount = totalFramesRead;
93269 }
93270 return pFrames;
93271 }
93272 DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
93273 {
93274 drmp3 mp3;
93275 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
93276 return NULL;
93277 }
93278 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
93279 }
93280 DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
93281 {
93282 drmp3 mp3;
93283 if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
93284 return NULL;
93285 }
93286 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
93287 }
93288 DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
93289 {
93290 drmp3 mp3;
93291 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
93292 return NULL;
93293 }
93294 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
93295 }
93296 DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
93297 {
93298 drmp3 mp3;
93299 if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
93300 return NULL;
93301 }
93302 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
93303 }
93304 #ifndef DR_MP3_NO_STDIO
93305 DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
93306 {
93307 drmp3 mp3;
93308 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
93309 return NULL;
93310 }
93311 return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
93312 }
93313 DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
93314 {
93315 drmp3 mp3;
93316 if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
93317 return NULL;
93318 }
93319 return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
93320 }
93321 #endif
93322 DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
93323 {
93324 if (pAllocationCallbacks != NULL) {
93325 return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks);
93326 } else {
93327 return drmp3__malloc_default(sz, NULL);
93328 }
93329 }
93330 DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
93331 {
93332 if (pAllocationCallbacks != NULL) {
93333 drmp3__free_from_callbacks(p, pAllocationCallbacks);
93334 } else {
93335 drmp3__free_default(p, NULL);
93336 }
93337 }
93338 #endif
93339 /* dr_mp3_c end */
93340 #endif /* DRMP3_IMPLEMENTATION */
93341 #endif /* MA_NO_MP3 */
93342
93343
93344 /* End globally disabled warnings. */
93345 #if defined(_MSC_VER)
93346 #pragma warning(pop)
93347 #endif
93348
93349 #endif /* miniaudio_c */
93350 #endif /* MINIAUDIO_IMPLEMENTATION */
93351
93352
93353 /*
93354 This software is available as a choice of the following licenses. Choose
93355 whichever you prefer.
93356
93357 ===============================================================================
93358 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
93359 ===============================================================================
93360 This is free and unencumbered software released into the public domain.
93361
93362 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
93363 software, either in source code form or as a compiled binary, for any purpose,
93364 commercial or non-commercial, and by any means.
93365
93366 In jurisdictions that recognize copyright laws, the author or authors of this
93367 software dedicate any and all copyright interest in the software to the public
93368 domain. We make this dedication for the benefit of the public at large and to
93369 the detriment of our heirs and successors. We intend this dedication to be an
93370 overt act of relinquishment in perpetuity of all present and future rights to
93371 this software under copyright law.
93372
93373 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
93374 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
93375 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93376 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
93377 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
93378 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
93379
93380 For more information, please refer to <http://unlicense.org/>
93381
93382 ===============================================================================
93383 ALTERNATIVE 2 - MIT No Attribution
93384 ===============================================================================
93385 Copyright 2023 David Reid
93386
93387 Permission is hereby granted, free of charge, to any person obtaining a copy of
93388 this software and associated documentation files (the "Software"), to deal in
93389 the Software without restriction, including without limitation the rights to
93390 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
93391 of the Software, and to permit persons to whom the Software is furnished to do
93392 so.
93393
93394 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
93395 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
93396 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93397 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
93398 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
93399 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
93400 SOFTWARE.
93401 */
93402